package org.marketcetera.saclient; import static org.junit.Assert.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.marketcetera.module.*; import org.marketcetera.util.except.I18NException; import org.marketcetera.util.file.CopyCharsUtils; import org.marketcetera.util.log.I18NMessage0P; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.ws.ContextClassProvider; import org.marketcetera.util.ws.wrappers.MapWrapper; /* $License$ */ /** * Tests {@link SAClient} web services. * <p/> * For each web service, verifies that the parameters to each web service * are correctly received and that the return values are correctly received. * And if the web service failed that the exception is correctly received. * * @author anshul@marketcetera.com * @version $Id: SAClientWSTest.java 16853 2014-03-06 02:10:11Z colin $ * @since 2.0.0 */ @ClassVersion("$Id: SAClientWSTest.java 16853 2014-03-06 02:10:11Z colin $") public class SAClientWSTest extends SAClientTestBase { /** * Run before each test. * * @throws Exception if an unexpected error occurs */ @Before public void before() throws Exception { super.before(); } @Test public void getProviders() throws Exception { //Test a non-empty and an empty list. List<List<ModuleURN>> lists = Arrays.asList(Arrays.asList( new ModuleURN("test:prov:A"), new ModuleURN("test:prov:B")), new ArrayList<ModuleURN>()); for (List<ModuleURN> list : lists) { final List<ModuleURN> urnList = list; testAPI(new WSTester<List<ModuleURN>>() { @Override protected List<ModuleURN> invokeApi(boolean isNullParams) throws Exception { return getClient().getProviders(); } @Override protected List<ModuleURN> setReturnValue(boolean isNullParams) { List<ModuleURN> moduleURNs = isNullParams ? null : urnList; getMockSAService().setURNList(moduleURNs); //nulls are returned as empty lists. return moduleURNs == null ? new ArrayList<ModuleURN>() : moduleURNs; } }); resetServiceParameters(); } } @Test public void getInstances() throws Exception { //Test a non-empty and an empty list. List<List<ModuleURN>> lists = Arrays.asList(Arrays.asList( new ModuleURN("test:prov:me:A"), new ModuleURN("test:prov:me:B")), new ArrayList<ModuleURN>()); final ModuleURN input = new ModuleURN("test:prov:me"); for (List<ModuleURN> list : lists) { final List<ModuleURN> urnList = list; testAPI(new WSTester<List<ModuleURN>>() { @Override protected List<ModuleURN> invokeApi(boolean isNullParams) throws Exception { return getClient().getInstances(isNullParams ? null : input); } @Override protected List<ModuleURN> setReturnValue(boolean isNullParams) { List<ModuleURN> moduleURNs = isNullParams ? null : urnList; getMockSAService().setURNList(moduleURNs); //nulls are returned as empty lists. return moduleURNs == null ? new ArrayList<ModuleURN>() : moduleURNs; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); resetServiceParameters(); } } @Test public void getModuleInfo() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); final ModuleInfo output = new ModuleInfo(input, ModuleState.STARTED, null, null, new Date(), new Date(), null, false, false, false, false, false, null, null, 0, false, 0); testAPI(new WSTester<ModuleInfo>() { @Override protected ModuleInfo invokeApi(boolean isNullParams) throws Exception { return getClient().getModuleInfo(isNullParams ? null : input); } @Override protected ModuleInfo setReturnValue(boolean isNullParams) { ModuleInfo value = isNullParams ? null : output; getMockSAService().setModuleInfo(value); return value; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); } @Test public void createStrategy() throws Exception { File f = File.createTempFile("strat", ".tst"); f.deleteOnExit(); CopyCharsUtils.copy("Test Strategy Contents".toCharArray(), f.getAbsolutePath()); final CreateStrategyParameters input = new CreateStrategyParameters( "instance", "strategy", "java", f, "key=value", false); final ModuleURN output = new ModuleURN("test:prov:me:A"); testAPI(new WSTester<ModuleURN>() { @Override protected ModuleURN invokeApi(boolean isNullParams) throws Exception { return getClient().createStrategy(isNullParams ? null : input); } @Override protected ModuleURN setReturnValue(boolean isNullParams) { ModuleURN value = isNullParams ? null : output; getMockSAService().setURN(value); return value; } @Override protected void verifyInputParams(boolean isNullParams) throws Exception { verifyEquals(isNullParams ? null : input, getMockSAService().getCreateStrategyParameters()); } }); //Test non-existent file behavior resetServiceParameters(); //Create the parameter and then delete the file. CreateStrategyParameters parms = new CreateStrategyParameters( "instance", "strategy", "java", f, "key=value", false); assertTrue(f.delete()); assertFalse(f.exists()); getClient().createStrategy(parms); InputStream file = getMockSAService().getCreateStrategyParameters().getStrategySource(); assertNotNull(file); //If the file doesn't exist at the time it's being sent, it comes out //as empty at the other end. assertEquals(0, IOUtils.toByteArray(file).length); } @Test public void getStrategyCreateParameters() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); File f = File.createTempFile("strat", ".tst"); f.deleteOnExit(); CopyCharsUtils.copy("Test Strategy Contents".toCharArray(), f.getAbsolutePath()); final CreateStrategyParameters output = new CreateStrategyParameters( "instance", "strategy", "java", f, "key=value", false); testAPI(new WSTester<CreateStrategyParameters>() { @Override protected CreateStrategyParameters invokeApi(boolean isNullParams) throws Exception { return getClient().getStrategyCreateParms(isNullParams ? null : input); } @Override protected CreateStrategyParameters setReturnValue(boolean isNullParams) { CreateStrategyParameters value = isNullParams ? null : output; getMockSAService().setCreateStrategyParameters(value); return value; } }); //Test non-existent file behavior resetServiceParameters(); //Create the parameter and then delete the file. getMockSAService().setCreateStrategyParameters( new CreateStrategyParameters("instance", "strategy", "java", f, "key=value", false)); assertTrue(f.delete()); assertFalse(f.exists()); InputStream file = getClient().getStrategyCreateParms(null).getStrategySource(); assertNotNull(file); //If the file doesn't exist at the time it's being sent, it comes out //as empty at the other end. assertEquals(0, IOUtils.toByteArray(file).length); } @Test public void start() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); testAPI(new WSTester<Void>() { @Override protected Void invokeApi(boolean isNullParams) throws Exception { getClient().start(isNullParams ? null : input); return null; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); } @Test public void stop() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); testAPI(new WSTester<Void>() { @Override protected Void invokeApi(boolean isNullParams) throws Exception { getClient().stop(isNullParams ? null : input); return null; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); } /** * Tests the ability to send data. * * @throws Exception if an unexpected error occurs */ @Test public void sendData() throws Exception { final TestData data = new TestData(); testAPI(new WSTester<Void>() { @Override protected Void invokeApi(boolean isNullParams) throws Exception { getClient().sendData(isNullParams ? null : data); return null; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : data, getMockSAService().getData()); } }); } @Test public void delete() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); testAPI(new WSTester<Void>() { @Override protected Void invokeApi(boolean isNullParams) throws Exception { getClient().delete(isNullParams ? null : input); return null; } @Override protected void verifyInputParams(boolean isNullParams) { assertEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); } @Test public void getProperties() throws Exception { final ModuleURN input = new ModuleURN("test:prov:me:A"); final Map<String, Object> m = new HashMap<String, Object>(); m.put("first", BigDecimal.TEN); m.put("second", "next"); m.put("third", 909); //Test a non-empty and an empty map. List<Map<String, Object>> maps = Arrays.asList(m, new HashMap<String, Object>()); for (Map<String, Object> map : maps) { final Map<String, Object> output = map; testAPI(new WSTester<Map<String, Object>>() { @Override protected Map<String, Object> invokeApi(boolean isNullParams) throws Exception { return getClient().getProperties(isNullParams ? null : input); } @Override protected Map<String, Object> setReturnValue(boolean isNullParams) { getMockSAService().setPropertiesOut(isNullParams ? null : new MapWrapper<String, Object>(output)); return isNullParams ? null : output; } @Override protected void verifyInputParams(boolean isNullParams) throws Exception { verifyEquals(isNullParams ? null : input, getMockSAService().getURN()); } }); resetServiceParameters(); } } @Test public void setProperties() throws Exception { final ModuleURN input1 = new ModuleURN("test:prov:me:A"); final Map<String, Object> i2 = new HashMap<String, Object>(); i2.put("first", BigDecimal.ONE); i2.put("second", "mnext"); i2.put("third", 999); //Test a non-empty and an empty map. List<Map<String, Object>> inputs = Arrays.asList(i2, new HashMap<String, Object>()); final Map<String, Object> out = new HashMap<String, Object>(); out.put("first", BigDecimal.TEN); out.put("second", "next"); out.put("third", 909); //Test a non-empty and an empty map. List<Map<String, Object>> outputs = Arrays.asList(out, new HashMap<String, Object>()); for (int i = 0; i < inputs.size(); i++) { final Map<String, Object> input2 = inputs.get(i); final Map<String, Object> output = outputs.get(i); testAPI(new WSTester<Map<String, Object>>() { @Override protected Map<String, Object> invokeApi(boolean isNullParams) throws Exception { return getClient().setProperties(isNullParams ? null : input1, isNullParams ? null : input2); } @Override protected Map<String, Object> setReturnValue(boolean isNullParams) { getMockSAService().setPropertiesOut(isNullParams ? null : new MapWrapper<String, Object>(output)); return isNullParams ? null : output; } @Override protected void verifyInputParams(boolean isNullParams) throws Exception { assertEquals(isNullParams ? null : input1, getMockSAService().getURN()); MapWrapper<String, Object> mapWrapper = getMockSAService().getPropertiesIn(); verifyEquals(isNullParams ? null : input2, mapWrapper == null ? null : mapWrapper.getMap()); } }); resetServiceParameters(); } } @After public void reset() { resetServiceParameters(); } /* (non-Javadoc) * @see org.marketcetera.saclient.SAClientTestBase#getContextClasses() */ @Override protected ContextClassProvider getContextClassProvider() { return new ContextClassProvider() { @Override public Class<?>[] getContextClasses() { return new Class<?>[] { TestData.class }; } }; } /** * Tests the API using the supplied tester instance. * First the API is tested with non-null parameters, then with null * parameters and finally the failure of the API is tested. * * @param inTester the tester to test the API invocation. * @param <R> the return type of the API. * @throws Exception if there were unexpected errors */ private static <R> void testAPI(final WSTester<R> inTester) throws Exception { //Test a regular invocation. R value = inTester.setReturnValue(false); verifyEquals(value, inTester.invokeApi(false)); inTester.verifyInputParams(false); //Test invocation with nulls resetServiceParameters(); value = inTester.setReturnValue(true); verifyEquals(value, inTester.invokeApi(true)); inTester.verifyInputParams(true); //Test a failure resetServiceParameters(); I18NException failure = new I18NException( new I18NMessage0P(Messages.LOGGER, "test")); getMockSAService().setFailure(failure); inTester.setReturnValue(false); ConnectionException e = new ExpectedFailure<ConnectionException>() { @Override protected void run() throws Exception { inTester.invokeApi(false); } }.getException(); assertNotNull(e.getCause()); assertEquals(failure, e.getCause()); //Verify that input parameters were received even when failure occured. inTester.verifyInputParams(false); //Test service interruption verifyInvocationCannotBeInterrupted(inTester); } private static <R> void verifyInvocationCannotBeInterrupted(final WSTester<R> inTester) throws Exception { resetServiceParameters(); getMockSAService().setSleep(true); inTester.setReturnValue(false); final Semaphore sema = new Semaphore(0); final AtomicReference<Exception> interruptFailure = new AtomicReference<Exception>(); Thread t = new Thread(){ @Override public void run() { sema.release(); try { inTester.invokeApi(false); } catch (Exception ex) { interruptFailure.set(ex); } } }; t.start(); //Wait for the thread to be started sema.acquire(); //Interrupt it as soon as it is found started t.interrupt(); //wait for it to end t.join(); //verify that we are not able to interrupt it assertNull("API invocation got interrupted!", interruptFailure.get()); } /** * Verifies the equality of the two objects. Performs special handling * of certain types. * * @param inExpected expected object. * @param inActual actual object. * @throws IOException if there were errors. */ private static void verifyEquals(Object inExpected, Object inActual) throws IOException { if (inExpected == null) { assertNull(inActual); } else { assertNotNull("Expected:" + inExpected, inActual); //special handling for certain types if (inExpected instanceof ModuleInfo) { ModuleInfo e = (ModuleInfo) inExpected; ModuleInfo a = (ModuleInfo) inActual; ModuleTestBase.assertModuleInfo(a, e.getURN(), e.getState(), e.getInitiatedDataFlows(), e.getParticipatingDataFlows(), e.isAutocreated(), e.isAutostart(), e.isReceiver(), e.isEmitter(), e.isFlowRequester()); assertEquals(e.getLastStartFailure(), a.getLastStartFailure()); assertEquals(e.getLastStopFailure(), a.getLastStopFailure()); assertEquals(e.getLockQueueLength(), a.getLockQueueLength()); assertEquals(e.getReadLockCount(), a.getReadLockCount()); assertEquals(e.isWriteLocked(), a.isWriteLocked()); } else if (inExpected instanceof Map) { //Convert both maps to the same type Map<?,?> e = new HashMap<Object,Object>((Map<?,?>) inExpected); Map<?,?> a = new HashMap<Object,Object>((Map<?,?>) inActual); assertEquals(e, a); } else if (inExpected instanceof CreateStrategyParameters) { CreateStrategyParameters e = (CreateStrategyParameters) inExpected; CreateStrategyParameters a = (CreateStrategyParameters) inActual; assertEquals(e.getInstanceName(), a.getInstanceName()); assertEquals(e.getStrategyName(), a.getStrategyName()); assertEquals(e.getLanguage(), a.getLanguage()); InputStream ein = e.getStrategySource(); InputStream ain = a.getStrategySource(); assertArrayEquals(IOUtils.toByteArray(ein), IOUtils.toByteArray(ain)); ein.close(); ain.close(); } else { assertEquals(inExpected, inActual); } } } /** * A class to aid testing the behavior of all SA client web services. * * @param <R> the return type of the web service being invoked. */ private abstract static class WSTester<R> { /** * Sets the return value for the API on {@link MockSAServiceImpl}. * * @param isNullParams if testing mode is testing null * parameters/return values. * @return the expected return value of the API call. */ protected R setReturnValue(boolean isNullParams) { return null; } /** * Invoke the WS API being tested. * * @param isNullParams if the testing mode is testing null * parameters/return values. * @return the actual value returned by the API invocation. * @throws Exception if there were errors. */ protected abstract R invokeApi(boolean isNullParams) throws Exception; /** * Verify the input parameter values received by {@link MockSAServiceImpl} * after the API invocation went through. * * @param isNullParams if the testing mode is testing null * parameters/return values. * @throws Exception if there were errors. */ protected void verifyInputParams(boolean isNullParams) throws Exception { } } /** * Test class used to demonstrate the ability to send data. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: SAClientWSTest.java 16853 2014-03-06 02:10:11Z colin $ * @since 2.2.0 */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class TestData { /** * Create a new TestData instance. */ public TestData() { attribute3 = new Date(); attribute2 = System.nanoTime(); attribute1 = String.valueOf(attribute3) + " and " + String.valueOf(attribute2); } /** * Get the attribute1 value. * * @return a <code>String</code> value */ public String getAttribute1() { return attribute1; } /** * Sets the attribute1 value. * * @param a <code>String</code> value */ public void setAttribute1(String inAttribute1) { attribute1 = inAttribute1; } /** * Get the attribute2 value. * * @return a <code>long</code> value */ public long getAttribute2() { return attribute2; } /** * Sets the attribute2 value. * * @param a <code>long</code> value */ public void setAttribute2(long inAttribute2) { attribute2 = inAttribute2; } /** * Get the attribute3 value. * * @return a <code>Date</code> value */ public Date getAttribute3() { return attribute3; } /** * Sets the attribute3 value. * * @param a <code>Date</code> value */ public void setAttribute3(Date inAttribute3) { attribute3 = inAttribute3; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((attribute1 == null) ? 0 : attribute1.hashCode()); result = prime * result + (int) (attribute2 ^ (attribute2 >>> 32)); result = prime * result + ((attribute3 == null) ? 0 : attribute3.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestData other = (TestData) obj; if (attribute1 == null) { if (other.attribute1 != null) return false; } else if (!attribute1.equals(other.attribute1)) return false; if (attribute2 != other.attribute2) return false; if (attribute3 == null) { if (other.attribute3 != null) return false; } else if (!attribute3.equals(other.attribute3)) return false; return true; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "TestData [attribute1=" + attribute1 + ", attribute2=" + attribute2 + ", attribute3=" + attribute3 + "]"; } /** * attribute1 value */ private String attribute1; /** * attribute2 value */ private long attribute2; /** * attribute3 value */ private Date attribute3; } }