package org.marketcetera.modules.remote.receiver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedList;
import javax.management.JMX;
import javax.security.auth.login.Configuration;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.marketcetera.event.LogEventLevel;
import org.marketcetera.module.CopierModule;
import org.marketcetera.module.CopierModuleFactory;
import org.marketcetera.module.DataFlowID;
import org.marketcetera.module.DataFlowInfo;
import org.marketcetera.module.DataRequest;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.module.MockConfigProvider;
import org.marketcetera.module.ModuleException;
import org.marketcetera.module.ModuleManager;
import org.marketcetera.module.ModuleState;
import org.marketcetera.module.ModuleTestBase;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.test.LogTestAssist;
/* $License$ */
/**
* Tests {@link ReceiverModule}.
*
* @author anshul@marketcetera.com
* @version $Id: ReceiverTest.java 16841 2014-02-20 19:59:04Z colin $
* @since 1.5.0
*/
@ClassVersion("$Id: ReceiverTest.java 16841 2014-02-20 19:59:04Z colin $")
public class ReceiverTest extends ModuleTestBase {
/**
* Verifies the provider and module infos.
*
* @throws Exception if there were unexpected errors
*/
@Test
public void info() throws Exception {
initManager();
assertProviderInfo(mManager, ReceiverFactory.PROVIDER_URN,
new String[0], new Class[0],
Messages.PROVIDER_DESCRIPTION.getText(),false, false);
assertModuleInfo(mManager, ReceiverFactory.INSTANCE_URN,
ModuleState.STARTED, null, null, false,
true, true, false, false);
}
/**
* Verifies the behavior when no URL value is specified.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void noURL() throws Exception {
initManager(new MockConfigProvider());
//Verify that the data can flow.
verifyDataFlow();
//But we cannot connect to the server
verifyNoConnectToReceiver();
//And that the log included the message on URL not being configured.
mLogAssist.assertSomeEvent(Level.INFO, null,
Messages.NO_URL_SPECIFIED_LOG.getText(), null);
}
/**
* Verifies failures that prevent module from getting started.
*
* @throws Exception if there were errors.
*/
@Test
public void initFailures() throws Exception {
//Specify incorrect URL
new ExpectedFailure<ModuleException>(Messages.ERROR_STARTING_MODULE){
@Override
protected void run() throws Exception {
initManager(configProviderWithURLValue(
"this is not a valid URL"));
}
};
verifyNoConnectToReceiver();
//Verify that we can start up with correct configuration
//after all these failures
info();
//And that we can connect to the server.
verifyConnectToReceiver();
//And that the log included the message on module being fully configured
mLogAssist.assertSomeEvent(Level.INFO, null,
Messages.RECIEVER_REMOTING_CONFIGURED.getText(DEFAULT_URL),
null);
}
/**
* Verifies participation of the module in data flows.
*
* @throws Exception if there were errors
*/
@Test
public void dataFlowSuccess() throws Exception {
verifyNoConnectToReceiver();
initManager();
verifyDataFlow();
verifyConnectToReceiver();
}
/**
* Verifies data flow behavior with null and non-serializable values.
*
* @throws Exception if there were errors.
*/
@Test
public void dataFlowFailures() throws Exception {
initManager();
Object[]objs = new Object[]{
Integer.MAX_VALUE,
null, //ignored, causes no errors
Arrays.asList(BigInteger.TEN),
new Object(), //not serializable, causes errors
BigDecimal.TEN,
Arrays.asList(new Object()) //not serializable, causes errors
};
CopierModule.SynchronousRequest req = new CopierModule.SynchronousRequest(objs);
//exhaust all the permits
req.semaphore.acquire();
DataFlowID flowID = mManager.createDataFlow(new DataRequest[]{
new DataRequest(CopierModuleFactory.INSTANCE_URN, req),
new DataRequest(ReceiverFactory.PROVIDER_URN)
});
//Wait for all the objects to get emitted
req.semaphore.acquire();
DataFlowInfo flowInfo = mManager.getDataFlowInfo(flowID);
assertFlowInfo(flowInfo, flowID, 2,
true, false, null, null);
assertFlowStep(flowInfo.getFlowSteps()[0],
CopierModuleFactory.INSTANCE_URN, true, 6, 0, null,
false, 0, 0, null, CopierModuleFactory.INSTANCE_URN, req.toString());
assertFlowStep(flowInfo.getFlowSteps()[1],
ReceiverFactory.INSTANCE_URN, false, 0, 0, null,
true, 6, 2, Messages.ERROR_WHEN_TRANSMITTING.getText(
String.valueOf(objs[objs.length - 1])),
ReceiverFactory.PROVIDER_URN, null);
}
/**
* Tests MXBean functions.
*
* @throws Exception if there were errors
*/
@Test
public void jmx() throws Exception {
initManager();
verifyBeanInfo(getMBeanServer().getMBeanInfo(
ReceiverFactory.INSTANCE_URN.toObjectName()));
final ReceiverModuleMXBean bean = JMX.newMXBeanProxy(getMBeanServer(),
ReceiverFactory.INSTANCE_URN.toObjectName(),
ReceiverModuleMXBean.class);
//Verify setters and getters
assertEquals(DEFAULT_URL, bean.getURL());
assertEquals(false, bean.isSkipJAASConfiguration());
assertLogLevel(bean, LogEventLevel.WARN);
//url cannot be updated when the module is running.
new ExpectedFailure<IllegalStateException>(
Messages.ILLEGAL_STATE_SET_URL.getText()){
@Override
protected void run() throws Exception {
bean.setURL("blah");
}
};
new ExpectedFailure<IllegalArgumentException>(
Messages.ILLEGAL_STATE_SET_SKIP_JAAS.getText()){
@Override
protected void run() throws Exception {
bean.setSkipJAASConfiguration(true);
}
};
//log level can be updated any time.
LogEventLevel logLevel = LogEventLevel.INFO;
bean.setLogLevel(logLevel);
assertLogLevel(bean, logLevel);
//Stop the module
mManager.stop(ReceiverFactory.INSTANCE_URN);
//Verify setters and getters
assertEquals(DEFAULT_URL, bean.getURL());
assertLogLevel(bean, logLevel);
//Verify that we can set the URL
String url = "myURL";
bean.setURL(url);
assertEquals(url, bean.getURL());
//that we can set it to null
bean.setURL(null);
assertEquals(null, bean.getURL());
//verify that we can set the log level as well
verifyLogLevels(bean);
logLevel = LogEventLevel.ERROR;
bean.setLogLevel(logLevel);
assertLogLevel(bean, logLevel);
//Verify log level validations
new ExpectedFailure<IllegalArgumentException>(
Messages.NULL_LEVEL_VALUE.getText(EnumSet.allOf(
LogEventLevel.class))){
@Override
protected void run() throws Exception {
bean.setLogLevel(null);
}
};
//Verify that we still can not set skip JAAS
new ExpectedFailure<IllegalArgumentException>(
Messages.ILLEGAL_STATE_SET_SKIP_JAAS.getText()){
@Override
protected void run() throws Exception {
bean.setSkipJAASConfiguration(true);
}
};
//Verify module start failure with an invalid URL
bean.setURL("invalidURL");
new ExpectedFailure<ModuleException>(Messages.ERROR_STARTING_MODULE){
@Override
protected void run() throws Exception {
mManager.start(ReceiverFactory.INSTANCE_URN);
}
};
//verify module state
assertModuleInfo(mManager, ReceiverFactory.INSTANCE_URN,
ModuleState.START_FAILED, null, null, false, true,
true, false, false);
//Verify module starts when a valid URL is specified
bean.setURL(DEFAULT_URL);
mManager.start(ReceiverFactory.INSTANCE_URN);
}
/**
* Verifies that JAAS Configuration is skipped when the
* corresponding module attribute is turned on.
*
* @throws Exception if there was an exception
*/
@Test(timeout = 10000)
public void skipJaasConfig() throws Exception {
MockConfigProvider prov = configProviderWithURLValue(DEFAULT_URL);
prov.addDefault(ReceiverFactory.INSTANCE_URN, "SkipJAASConfiguration", "true");
//The module doesn't fail to start if the credentials are incorrect
//as JMS doesn't generate failure unless we attempt to send some data.
initManager(prov);
final ReceiverModuleMXBean bean = JMX.newMXBeanProxy(getMBeanServer(),
ReceiverFactory.INSTANCE_URN.toObjectName(),
ReceiverModuleMXBean.class);
assertEquals(true, bean.isSkipJAASConfiguration());
//Setup a data flow to force a connection and see what happens
final int data = Integer.MAX_VALUE;
DataFlowID flowID = mManager.createDataFlow(new DataRequest[]{
new DataRequest(CopierModuleFactory.INSTANCE_URN, new Object[]{
data
}),
new DataRequest(ReceiverFactory.PROVIDER_URN)
});
DataFlowInfo flowInfo = mManager.getDataFlowInfo(flowID);
assertFlowInfo(flowInfo, flowID, 2, true, false, null, null);
assertEquals(2, flowInfo.getFlowSteps().length);
while(flowInfo.getFlowSteps()[1].getLastReceiveError() == null) {
Thread.sleep(100);
flowInfo = mManager.getDataFlowInfo(flowID);
}
assertFlowStep(flowInfo.getFlowSteps()[1],
ReceiverFactory.INSTANCE_URN, false, 0, 0, null,
true, 1, 1, Messages.ERROR_WHEN_TRANSMITTING.getText(
String.valueOf(data)), ReceiverFactory.PROVIDER_URN, null);
//cancel the flow
mManager.cancel(flowID);
}
/**
* Verifies that the log level can be overridden by supplying a default
* value.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void specifyDefaultLogLevel() throws Exception {
MockConfigProvider prov = configProviderWithURLValue(DEFAULT_URL);
prov.addDefault(ReceiverFactory.INSTANCE_URN, "LogLevel", "DEBUG");
initManager(prov);
final ReceiverModuleMXBean bean = JMX.newMXBeanProxy(getMBeanServer(),
ReceiverFactory.INSTANCE_URN.toObjectName(),
ReceiverModuleMXBean.class);
assertLogLevel(bean, LogEventLevel.DEBUG);
}
/**
* Stops the module manager.
*
* @throws Exception if there were failures.
*/
@After
public void stopManager() throws Exception {
if (mManager != null) {
mManager.stop();
mManager = null;
}
mLogAssist.resetAppender();
}
/**
* This is needed for {@link #skipJaasConfig()} test to pass.
*/
@Before
public void resetJAASConfig() {
Configuration.setConfiguration(null);
}
/**
* Tests a connection to the receiver server.
*
* @throws IOException if the connection failed.
*/
private void verifyConnectToReceiver() throws IOException {
new Socket(DEFAULT_HOST, DEFAULT_PORT).close();
}
/**
* Verifies that the server for accepting remote connections
* is not created.
*
* @throws Exception if there were unexpected results.
*/
private void verifyNoConnectToReceiver() throws Exception {
new ExpectedFailure<ConnectException>(){
@Override
protected void run() throws Exception {
verifyConnectToReceiver();
}
};
}
/**
* Verifies data flow to the receiver.
*
* @throws Exception if there were unexpected failures.
*/
private void verifyDataFlow() throws Exception {
Object[]objs = new Object[]{
Integer.MAX_VALUE,
BigInteger.TEN,
BigDecimal.TEN,
new LinkedList<>(),
new HashMap<>()
};
CopierModule.SynchronousRequest req = new CopierModule.SynchronousRequest(objs);
//exhaust all the permits
req.semaphore.acquire();
DataFlowID flowID = mManager.createDataFlow(new DataRequest[]{
new DataRequest(CopierModuleFactory.INSTANCE_URN, req),
new DataRequest(ReceiverFactory.PROVIDER_URN)
});
//Wait for all the objects to get emitted
req.semaphore.acquire();
DataFlowInfo flowInfo = mManager.getDataFlowInfo(flowID);
assertFlowInfo(flowInfo, flowID, 2,
true, false, null, null);
assertFlowStep(flowInfo.getFlowSteps()[0],
CopierModuleFactory.INSTANCE_URN, true, 5, 0, null,
false, 0, 0, null, CopierModuleFactory.INSTANCE_URN, req.toString());
assertFlowStep(flowInfo.getFlowSteps()[1],
ReceiverFactory.INSTANCE_URN, false, 0, 0, null,
true, 5, 0, null, ReceiverFactory.PROVIDER_URN, null);
}
/**
* Tests all supported log levels and their translation to log4j levels.
*
* @param inBean the bean for the module
*/
private void verifyLogLevels(ReceiverModuleMXBean inBean) {
for(LogEventLevel level: LogEventLevel.values()) {
inBean.setLogLevel(level);
assertLogLevel(inBean, level);
}
}
/**
* Verifies that the supplied loglevel matches the bean current
* attribute value and the current log4j user messages logger
* configuration.
*
* @param inMBean the receiver's bean proxy.
* @param inLogLevel the expected log level.
*/
private void assertLogLevel(ReceiverModuleMXBean inMBean,
LogEventLevel inLogLevel) {
assertEquals(inLogLevel, inMBean.getLogLevel());
Level level = LogManager.getLogger(
org.marketcetera.core.Messages.USER_MSG_CATEGORY).getLevel();
assertNotNull(level);
assertEquals(inLogLevel.toString(), level.toString());
}
/**
* Initializes the module manager with the default URL value for the
* receiver module.
*
* @throws Exception if there were errors.
*/
private void initManager() throws Exception {
MockConfigProvider prov = configProviderWithURLValue(DEFAULT_URL);
initManager(prov);
}
/**
* Initializes the module manager with the supplied configuration provider.
*
* @param inProvider the configuration provider instance.
*
* @throws Exception if there were errors.
*/
private void initManager(MockConfigProvider inProvider) throws Exception {
mManager = new ModuleManager();
mManager.setConfigurationProvider(inProvider);
mManager.init();
}
/**
* Creates a configuration provider instance with the supplied URL
* value as the default URL value for the receiver module.
*
* @param inUrl the URL value.
*
* @return the configuration provider instance.
*/
private MockConfigProvider configProviderWithURLValue(String inUrl) {
MockConfigProvider prov = new MockConfigProvider();
prov.addDefault(ReceiverFactory.INSTANCE_URN, "URL", inUrl);
return prov;
}
private ModuleManager mManager;
private final LogTestAssist mLogAssist = new LogTestAssist(ReceiverModule.class.getName(),Level.INFO);
/**
* The default URL value to run the receiver's embedded broker on.
*/
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 61617;
private static final String DEFAULT_URL = "tcp://" + DEFAULT_HOST + ":" + DEFAULT_PORT;
}