package org.marketcetera.strategyagent; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.*; import java.io.File; import java.math.BigDecimal; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.management.InstanceNotFoundException; import javax.management.InvalidAttributeValueException; import org.hamcrest.Matchers; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.marketcetera.client.ClientManager; import org.marketcetera.client.ClientModuleFactory; import org.marketcetera.client.ClientParameters; import org.marketcetera.client.MockServer; import org.marketcetera.core.ApplicationVersion; import org.marketcetera.core.Util; import org.marketcetera.module.ExpectedFailure; import org.marketcetera.module.ModuleState; import org.marketcetera.module.ModuleTestBase; import org.marketcetera.module.ModuleURN; import org.marketcetera.saclient.*; import org.marketcetera.strategy.Language; import org.marketcetera.util.except.I18NException; import org.marketcetera.util.file.Deleter; import org.marketcetera.util.log.I18NBoundMessage; import org.marketcetera.util.log.I18NBoundMessage1P; import org.marketcetera.util.log.I18NBoundMessage3P; import org.marketcetera.util.ws.stateful.Authenticator; import org.marketcetera.util.ws.stateless.Node; import org.marketcetera.util.ws.stateless.StatelessClientContext; import org.marketcetera.util.ws.wrappers.RemoteProperties; import com.google.common.collect.Maps; /* $License$ */ /** * Verifies Remoting capabilities of the strategy agent. * * @author anshul@marketcetera.com * @version $Id: StrategyAgentRemotingTest.java 16879 2014-04-15 21:40:25Z colin $ * @since 2.0.0 */ public class StrategyAgentRemotingTest extends StrategyAgentTestBase { /** * Initializes the mock server, its client connection and the strategy * agent so that remote receiver is able to authenticate its clients * successfully. * * @throws Exception if there were unexpected failures. */ @BeforeClass public static void createServerAndClient() throws Exception { StrategyAgentRemotingConfigTest.setupConfiguration(); sServer = new MockServer(); ClientManager.init(new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT)); useWs = true; createSaWith(); } /** * Closes the client connection and shuts down the mock server. * * @throws Exception if there were errors. */ @AfterClass public static void stopServerAndClient() throws Exception { if(ClientManager.isInitialized()) { ClientManager.getInstance().close(); } if (sServer != null) { sServer.close(); } shutdownSa(); } /** * Closes the SA client connection if it's active. */ @After public void closeClient() { if(sSAClient != null) { sSAClient.close(); sSAClient = null; } for(ModuleURN strategyInstance : moduleManager.getModuleInstances(STRATEGY_PROVIDER_URN)) { try { moduleManager.stop(strategyInstance); } catch (Exception ignored) {} try { moduleManager.deleteModule(strategyInstance); } catch (Exception ignored) {} } } /** * Tests failures due to user authentication failure. * * @throws Exception if there were unexpected errors. */ @Test public void loginFailures() throws Exception { //test null password new ExpectedFailure<ConnectionException>( org.marketcetera.saclient.Messages.ERROR_WS_CONNECT){ @Override protected void run() throws Exception { SAClientFactoryImpl.getInstance().create( new SAClientParameters(DEFAULT_CREDENTIAL, null, RECEIVER_URL, WS_HOST, WS_PORT)).start(); } }; //empty password new ExpectedFailure<ConnectionException>( org.marketcetera.saclient.Messages.ERROR_WS_CONNECT){ @Override protected void run() throws Exception { SAClientFactoryImpl.getInstance().create( new SAClientParameters(DEFAULT_CREDENTIAL, "".toCharArray(), RECEIVER_URL, WS_HOST, WS_PORT)).start(); } }; //incorrect password new ExpectedFailure<ConnectionException>( org.marketcetera.saclient.Messages.ERROR_WS_CONNECT){ @Override protected void run() throws Exception { SAClientFactoryImpl.getInstance().create( new SAClientParameters(DEFAULT_CREDENTIAL, "what?".toCharArray(), RECEIVER_URL, WS_HOST, WS_PORT)).start(); } }; //incorrect username new ExpectedFailure<ConnectionException>( org.marketcetera.saclient.Messages.ERROR_WS_CONNECT){ @Override protected void run() throws Exception { SAClientFactoryImpl.getInstance().create( new SAClientParameters("who", DEFAULT_CREDENTIAL.toCharArray(), RECEIVER_URL, WS_HOST, WS_PORT)).start(); } }; //finally a successful login createClient(); } /** * Tests {@link StrategyAgent#authenticate(org.marketcetera.util.ws.stateless.StatelessClientContext, String, char[])} * * @throws Exception if there were unexpected failures. */ @Test public void clientAuth() throws Exception { final Authenticator authenticator = new DefaultAuthenticator(); //null context new ExpectedFailure<NullPointerException>(){ @Override protected void run() throws Exception { authenticator.shouldAllow(null,"value", "value".toCharArray()); } }; final StatelessClientContext ctx = new StatelessClientContext(); assertNull(ctx.getAppId()); //context without appID assertTrue(authenticator.shouldAllow(ctx, DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray())); //context with invalid appID ctx.setAppId(Util.getAppId("invalid", ApplicationVersion.getVersion().getVersionInfo())); assertTrue(authenticator.shouldAllow(ctx, DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray())); //context with correct name & version number ctx.setAppId(Util.getAppId(SAClientVersion.APP_ID_NAME, ApplicationVersion.getVersion().getVersionInfo())); assertTrue(authenticator.shouldAllow(ctx, DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray())); //valid contexts //invalid user/password assertFalse(authenticator.shouldAllow(ctx,"go","go".toCharArray())); //invalid password assertFalse(authenticator.shouldAllow(ctx,DEFAULT_CREDENTIAL,"go".toCharArray())); //null password assertFalse(authenticator.shouldAllow(ctx,DEFAULT_CREDENTIAL,null)); //invalid user assertFalse(authenticator.shouldAllow(ctx,"go",DEFAULT_CREDENTIAL.toCharArray())); //null user assertFalse(authenticator.shouldAllow(ctx,null,DEFAULT_CREDENTIAL.toCharArray())); } @Test public void getInstances() throws Exception { final SAClient saClient = createClient(); List<ModuleURN> urns = saClient.getInstances(null); assertFalse(urns.toString(), urns.isEmpty()); //verify it contains the receiver and the client instances assertTrue(urns.toString(), urns.contains(ClientModuleFactory.INSTANCE_URN)); assertTrue(urns.toString(), urns.contains(RECEIVER_URN)); //try sending a URN of a provider that doesn't exist urns = saClient.getInstances(new ModuleURN("metc:not:exist")); assertTrue(urns.toString(), urns.isEmpty()); //try sending a URN of a provider that has some instances urns = saClient.getInstances(RECEIVER_URN.parent()); assertFalse(urns.toString(), urns.isEmpty()); assertEquals(urns.toString(), 1, urns.size()); assertTrue(urns.toString(), urns.contains(RECEIVER_URN)); } @Test public void getProviders() throws Exception { SAClient saClient = createClient(); List<ModuleURN> urns = saClient.getProviders(); assertFalse(urns.toString(), urns.isEmpty()); assertTrue(urns.toString(), urns.contains(ClientModuleFactory.INSTANCE_URN.parent())); assertTrue(urns.toString(), urns.contains(RECEIVER_URN.parent())); } @Test public void getModuleInfo() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.getModuleInfo(null); } }); //non-existent module's URN final String urn = "metc:instance:not:exist"; ConnectionException failure = new WSOpFailure() { @Override protected void run() throws Exception { saClient.getModuleInfo(new ModuleURN(urn)); } }.getException(); assertEquals(failure.getCause().getLocalizedMessage(), org.marketcetera.module.Messages.MODULE_NOT_FOUND.getText(urn)); //valid URN ModuleTestBase.assertModuleInfo(saClient.getModuleInfo(RECEIVER_URN), RECEIVER_URN, ModuleState.STARTED, null, null, false, true, true, false, false); } @Test public void getPropertiesFailure() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.getProperties(null); } }); //non-existent module final String urn = "metc:instance:not:exist"; ConnectionException failure = new WSOpFailure(){ @Override protected void run() throws Exception { saClient.getProperties(new ModuleURN(urn)); } }.getException(); assertThat(failure.getCause(), Matchers.instanceOf(InstanceNotFoundException.class)); assertEquals(failure.getCause().getMessage(), new ModuleURN(urn).toObjectName().toString()); } @Test public void setPropertiesFailure() throws Exception { final SAClient saClient = createClient(); // null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.setProperties(null, null); } }); // non strategy URN verifyNestedFailure(new I18NBoundMessage1P(Messages.SET_PROPERTY_MODULE_NOT_STRATEGY, RECEIVER_URN), new WSOpFailure() { @Override protected void run() throws Exception { saClient.setProperties(RECEIVER_URN, null); } }); // non existent strategy final ModuleURN urn = new ModuleURN(STRATEGY_PROVIDER_URN, "notexist"); ConnectionException failure = new WSOpFailure(){ @Override protected void run() throws Exception { //Supply a non-null property value Map<String, Object> propMap = new HashMap<String, Object>(); saClient.setProperties(urn, propMap); } }.getException(); assertThat(failure.getCause(), Matchers.instanceOf(InstanceNotFoundException.class)); assertEquals(failure.getCause().getMessage(), urn.toObjectName().toString()); //non editable properties final String key = "OutputDestination"; verifyNestedFailure(new I18NBoundMessage3P( Messages.UNEDITABLE_STRATEGY_PROPERTY, key, urn, SAServiceImpl.EDITABLE_STRATEGY_PROPERTIES.toString()), new WSOpFailure() { @Override protected void run() throws Exception { Map<String, Object> propMap = new HashMap<String, Object>(); propMap.put(key, "value"); saClient.setProperties(urn, propMap); } }); } /** * Tests failures when creating strategy. * * @throws Exception if there were unexpected errors. */ @Test public void createStrategyFailure() throws Exception { final SAClient saClient = createClient(); //null parameter failure verifyNestedFailure(Messages.NO_STRATEGY_CREATE_PARMS_SPECIFIED, new WSOpFailure() { @Override protected void run() throws Exception { saClient.createStrategy(null); } }); //Create a temp file final File scriptFile = File.createTempFile("script",".tmp"); final String name = "Strat"; //Test failure from specifying an incorrect language. ConnectionException failure = new WSOpFailure() { @Override protected void run() throws Exception { saClient.createStrategy(new CreateStrategyParameters(name, "HelloWorld", "UBY", scriptFile, null, false)); } }.getException(); //verify that we failed to convert the language. ExpectedFailure.assertI18NException(failure.getCause(), org.marketcetera.strategy.Messages.INVALID_LANGUAGE_ERROR, "UBY"); //Test for not-existing files CreateStrategyParameters parameters = new CreateStrategyParameters(name, "HelloWorld", "RUBY", scriptFile, null, false); //now delete the script file Deleter.apply(scriptFile); //Create strategy succeeds final ModuleURN urn = saClient.createStrategy(parameters); assertEquals(name, urn.instanceName()); //But we get an error when we try to start it because the strategy file is empty failure = new WSOpFailure() { @Override protected void run() throws Exception { saClient.start(urn); } }.getException(); assertThat(failure.getCause().getLocalizedMessage(), Matchers.containsString(org.marketcetera.strategy.Messages.FAILED_TO_START.getText())); } /** * Tests failures when fetching strategy create parameters. * * @throws Exception if there were unexpected errors. */ @Test public void getStrategyCreateParmsFailure() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.getStrategyCreateParms(null); } }); //non strategy URN verifyNestedFailure(new I18NBoundMessage1P( Messages.NO_CREATE_PARAMETERS_FOR_STRATEGY, RECEIVER_URN), new WSOpFailure() { @Override protected void run() throws Exception { saClient.getStrategyCreateParms(RECEIVER_URN); } }); //non existent strategy final ModuleURN urn = new ModuleURN(STRATEGY_PROVIDER_URN, "notexist"); verifyNestedFailure(new I18NBoundMessage1P( Messages.NO_CREATE_PARAMETERS_FOR_STRATEGY, urn), new WSOpFailure() { @Override protected void run() throws Exception { saClient.getStrategyCreateParms(urn); } }); } @Test public void startFailure() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.start(null); } }); //non strategy URN verifyNestedFailure(new I18NBoundMessage1P( Messages.START_MODULE_NOT_STRATEGY, RECEIVER_URN), new WSOpFailure() { @Override protected void run() throws Exception { saClient.start(RECEIVER_URN); } }); //non strategy module final ModuleURN urn = new ModuleURN(STRATEGY_PROVIDER_URN, "notexist"); ConnectionException failure = new WSOpFailure(){ @Override protected void run() throws Exception { saClient.start(urn); } }.getException(); assertThat(failure.getCause().getLocalizedMessage(), Matchers.containsString(org.marketcetera.module.Messages.MODULE_NOT_FOUND.getText(urn))); } @Test public void stopFailure() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.stop(null); } }); //non strategy URN verifyNestedFailure(new I18NBoundMessage1P( Messages.STOP_MODULE_NOT_STRATEGY, RECEIVER_URN), new WSOpFailure() { @Override protected void run() throws Exception { saClient.stop(RECEIVER_URN); } }); //non strategy module final ModuleURN urn = new ModuleURN(STRATEGY_PROVIDER_URN, "notexist"); ConnectionException failure = new WSOpFailure(){ @Override protected void run() throws Exception { saClient.stop(urn); } }.getException(); assertThat(failure.getCause().getLocalizedMessage(), Matchers.containsString(org.marketcetera.module.Messages.MODULE_NOT_FOUND.getText(urn))); } @Test public void deleteFailure() throws Exception { final SAClient saClient = createClient(); //null URN verifyNullURNFailure(new WSOpFailure() { @Override protected void run() throws Exception { saClient.delete(null); } }); //non strategy URN verifyNestedFailure(new I18NBoundMessage1P( Messages.DELETE_MODULE_NOT_STRATEGY, RECEIVER_URN), new WSOpFailure() { @Override protected void run() throws Exception { saClient.delete(RECEIVER_URN); } }); //non strategy module final ModuleURN urn = new ModuleURN(STRATEGY_PROVIDER_URN, "notexist"); ConnectionException failure = new WSOpFailure(){ @Override protected void run() throws Exception { saClient.delete(urn); } }.getException(); assertThat(failure.getCause().getLocalizedMessage(), Matchers.containsString(org.marketcetera.module.Messages.MODULE_NOT_FOUND.getText(urn))); } /** * Tests strategy lifecycle and various methods that can be successfully * invoked after the strategy has been created. * * @throws Exception if there were unexpected errors. */ @Test public void strategyLifecycle() throws Exception { final SAClient saClient = createClient(); // create, start, stop, delete, get/set props, get createParms // verify no instances exist yet List<ModuleURN> instances = saClient.getInstances(STRATEGY_PROVIDER_URN); assertTrue(instances.toString(), instances.isEmpty()); // create a strategy assertTrue(TEST_STRATEGY.getAbsolutePath(), TEST_STRATEGY.isFile()); String name = "myStrat"; ModuleURN urn = saClient.createStrategy(new CreateStrategyParameters(name, "HelloWorld", "RUBY", TEST_STRATEGY, null, false)); assertEquals(name, urn.instanceName()); assertEquals(STRATEGY_PROVIDER_URN, urn.parent()); // verify the instance is reported instances = saClient.getInstances(STRATEGY_PROVIDER_URN); assertEquals(instances.toString(), 1, instances.size()); assertEquals(urn, instances.get(0)); // verify strategy properties and state ModuleTestBase.assertModuleInfo(saClient.getModuleInfo(urn), urn, ModuleState.CREATED, null, null, false, false, true, true, true); Map<String,String> props = toStringProps(saClient.getProperties(urn)); assertNotNull(props); assertFalse(props.isEmpty()); assertEquals(props.toString(), 5, props.size()); assertThat(props, allOf(hasEntry("Parameters", null), hasEntry("Name", "HelloWorld"), hasEntry("Language", Language.RUBY.toString()), hasEntry(STRAT_PROP_ROUTING_ORDERS, String.valueOf(false)), hasEntry("OutputDestination", RECEIVER_URN.parent().getValue()))); // start the strategy saClient.start(urn); // verify properties and state assertEquals(ModuleState.STARTED, saClient.getModuleInfo(urn).getState()); props = toStringProps(saClient.getProperties(urn)); assertNotNull(props); assertEquals(props.toString(), 6, props.size()); assertThat(props, allOf(hasEntry("Parameters", null), hasEntry("Name", "HelloWorld"), hasEntry("Language", Language.RUBY.toString()), hasEntry(STRAT_PROP_ROUTING_ORDERS, String.valueOf(false)), hasEntry("Status", "RUNNING"), hasEntry("OutputDestination", RECEIVER_URN.parent().getValue()))); // stop the strategy saClient.stop(urn); // verify properties and state assertEquals(ModuleState.STOPPED, saClient.getModuleInfo(urn).getState()); // change properties props = Maps.newHashMap(); String paramValue = "key1=value1"; props.put("Parameters", paramValue); Map<String,Object> actualProps = toObjectProps(props); actualProps.put(STRAT_PROP_ROUTING_ORDERS, true); props = toStringProps(saClient.setProperties(urn, actualProps)); assertNotNull(props); assertEquals(props.toString(), 2, props.size()); assertThat(props, allOf(hasEntry("Parameters", String.valueOf(paramValue)), hasEntry(STRAT_PROP_ROUTING_ORDERS, String.valueOf(true)))); // verify that the property indeed changed by fetching them again props = toStringProps(saClient.getProperties(urn)); assertNotNull(props); assertEquals(props.toString(), 6, props.size()); assertThat(props, allOf(hasEntry("Parameters", String.valueOf(paramValue)), hasEntry("Name", "HelloWorld"), hasEntry("Language", Language.RUBY.toString()), hasEntry(STRAT_PROP_ROUTING_ORDERS, String.valueOf(true)), hasEntry("Status", "STOPPED"), hasEntry("OutputDestination", RECEIVER_URN.parent().getValue()))); props.clear(); actualProps.clear(); actualProps.put(STRAT_PROP_ROUTING_ORDERS, BigDecimal.ONE); actualProps = saClient.setProperties(urn, actualProps); assertNotNull(actualProps); assertEquals(1, actualProps.size()); assertThat(actualProps, Matchers.hasKey(STRAT_PROP_ROUTING_ORDERS)); Object err = actualProps.get(STRAT_PROP_ROUTING_ORDERS); assertThat(err, Matchers.instanceOf(RemoteProperties.class)); RemoteProperties prop = (RemoteProperties)err; // verify the we had the expected failure. assertThat(prop.getServerString(), Matchers.containsString(InvalidAttributeValueException.class.getName())); // delete strategy saClient.delete(urn); // verify it's not reported any more instances = saClient.getInstances(STRATEGY_PROVIDER_URN); assertTrue(instances.toString(), instances.isEmpty()); } /** * Transforms the given map to a map with object values. * * @param inProperties a <code>Map<String,String></code> value * @return a <code>Map<String,Object></code> value */ private Map<String,Object> toObjectProps(Map<String,String> inProperties) { if(inProperties == null) { return null; } Map<String,Object> output = Maps.newHashMap(); for(Map.Entry<String,String> entry : inProperties.entrySet()) { output.put(entry.getKey(),entry.getValue()); } return output; } /** * Transforms the given map to a map with string values. * * @param inProperties a <code>Map<String,Object></code> value * @return a <code>Map<String,String></code> value */ private Map<String,String> toStringProps(Map<String,Object> inProperties) { if(inProperties == null) { return null; } Map<String,String> output = Maps.newHashMap(); for(Map.Entry<String,Object> entry : inProperties.entrySet()) { output.put(entry.getKey(),entry.getValue() == null ? null : String.valueOf(entry.getValue())); } return output; } private void verifyNullURNFailure(final WSOpFailure inTest) throws Exception { verifyNestedFailure(Messages.CANNOT_PROCESS_NULL_URN, inTest); } private void verifyNestedFailure(I18NBoundMessage inMessage, WSOpFailure inTest) throws Exception { assertEquals(inMessage, ((I18NException) inTest.getException(). getCause()).getI18NBoundMessage()); } private static SAClient createClient() throws ConnectionException { sSAClient = SAClientFactoryImpl.getInstance().create(new SAClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), RECEIVER_URL, WS_HOST, WS_PORT)); sSAClient.start(); return sSAClient; } /** * A class to test WS operation failure */ private static abstract class WSOpFailure extends ExpectedFailure<ConnectionException> { protected WSOpFailure() throws Exception { super(org.marketcetera.saclient.Messages.ERROR_WS_OPERATION); } } private static MockServer sServer; private static final String DEFAULT_CREDENTIAL = "DrNo"; private static final String WS_HOST = "localhost"; private static final int WS_PORT = 9001; private static final int JMS_PORT = 61617; private static final String RECEIVER_URL = "tcp://" + WS_HOST + ":" + JMS_PORT; private static final File TEST_STRATEGY = new File("src/test/sample_data/test_strategy.rb"); private static volatile SAClient sSAClient; private static final ModuleURN RECEIVER_URN = new ModuleURN("metc:remote:receiver:single"); private static final ModuleURN STRATEGY_PROVIDER_URN = new ModuleURN("metc:strategy:system"); private static final String STRAT_PROP_ROUTING_ORDERS = "RoutingOrdersToORS"; }