package org.marketcetera.client; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.log.I18NBoundMessage1P; import org.marketcetera.util.log.I18NBoundMessage2P; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.except.I18NException; import org.marketcetera.module.*; import org.marketcetera.trade.*; import org.marketcetera.metrics.ThreadedMetric; import org.apache.commons.lang.ObjectUtils; import java.util.Map; import java.util.Hashtable; import java.util.Date; /* $License$ */ /** * The module that sends orders to ORS and emits reports * received from ORS. * <p> * The module only accepts data of following types * <ul> * <li>{@link org.marketcetera.trade.OrderSingle}</li> * <li>{@link org.marketcetera.trade.OrderCancel}</li> * <li>{@link org.marketcetera.trade.OrderReplace}</li> * <li>{@link org.marketcetera.trade.FIXOrder}</li> * </ul> * <p> * The module will emit all the reports from the server when requested. * Following types of reports may be emitted by the module. * <ul> * <li>{@link org.marketcetera.trade.ExecutionReport}</li> * <li>{@link org.marketcetera.trade.OrderCancelReject}</li> * </ul> * <p> * Module Features * <table> * <tr><th>Capabilities</th><td>Data Emitter, Data Receiver</td></tr> * <tr><th>DataFlow Request Parameters</th><td>None, Should be null.</td></tr> * <tr><th>Stops data flows</th><td>Yes, if the client is not initialized</td></tr> * <tr><th>Start Operation</th><td>Does nothing</td></tr> * <tr><th>Stop Operation</th><td>Does nothing</td></tr> * <tr><th>Management Interface</th><td>{@link ClientModuleMXBean}</td></tr> * <tr><th>Factory</th><td>{@link ClientModuleFactory}</td></tr> * </table> * * @author anshul@marketcetera.com * @version $Id: ClientModule.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: ClientModule.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$ class ClientModule extends Module implements DataReceiver, DataEmitter, ClientModuleMXBean { @Override public void receiveData(DataFlowID inFlowID, Object inData) throws ReceiveDataException { ThreadedMetric.event("client-IN"); //$NON-NLS-1$ try { if(inData instanceof OrderSingle) { getClient().sendOrder((OrderSingle) inData); } else if(inData instanceof OrderCancel) { getClient().sendOrder((OrderCancel) inData); } else if(inData instanceof OrderReplace) { getClient().sendOrder((OrderReplace) inData); } else if(inData instanceof FIXOrder) { getClient().sendOrderRaw((FIXOrder) inData); } else { throw new UnsupportedDataTypeException(new I18NBoundMessage2P( Messages.UNSUPPORTED_DATA_TYPE, inFlowID.getValue(), ObjectUtils.toString(inData))); } } catch (ConnectionException e) { throw new ReceiveDataException(e, new I18NBoundMessage1P( Messages.SEND_ORDER_FAIL_NO_CONNECT, ObjectUtils.toString(inData))); } catch (OrderValidationException e) { throw new ReceiveDataException(e, new I18NBoundMessage2P( Messages.SEND_ORDER_VALIDATION_FAILED, inFlowID.getValue(), ObjectUtils.toString(inData))); } catch (ClientInitException e) { throw new StopDataFlowException(e, new I18NBoundMessage1P( Messages.SEND_ORDER_FAIL_NO_CONNECT, ObjectUtils.toString(inData))); } } @Override public void requestData(DataRequest inRequest, final DataEmitterSupport inSupport) throws RequestDataException { //No request parameters are supported. //All reports received are emitted. //Verify no request parameters are specified if(inRequest.getData() != null) { throw new IllegalRequestParameterValue( Messages.REQUEST_PARAMETER_SPECIFIED); } try { ReportListener listener = new ReportListenerEmitter(inSupport); getClient().addReportListener(listener); mRequestTable.put(inSupport.getRequestID(), listener); } catch (ClientInitException e) { throw new RequestDataException(e, Messages.REQUEST_CLIENT_NOT_INITIALIZED); } } @Override public void cancel(DataFlowID inFlowID, RequestID inRequestID) { ReportListener listener = mRequestTable.remove(inRequestID); try { getClient().removeReportListener(listener); } catch (ClientInitException e) { Messages.LOG_CLIENT_NOT_INIT_CANCEL_REQUEST.error(this, e, inRequestID.toString()); } } @Override public void reconnect() throws RuntimeException { try { getClient().reconnect(); } catch (I18NException e) { throw new RuntimeException(e.getLocalizedDetail()); } } @Override public ClientParameters getParameters() { try { return getClient().getParameters(); } catch (ClientInitException e) { throw new RuntimeException(e.getLocalizedDetail()); } } @Override public Date getLastConnectTime() throws RuntimeException { try { return getClient().getLastConnectTime(); } catch (ClientInitException e) { throw new RuntimeException(e.getLocalizedDetail()); } } /** * Creates an instance. * * @param inURN The instance URN * @param inAutoStart if the module should be auto-started. */ protected ClientModule(ModuleURN inURN, boolean inAutoStart) { super(inURN, inAutoStart); } @Override protected void preStart() throws ModuleException { } @Override protected void preStop() throws ModuleException { } private Client getClient() throws ClientInitException { return ClientManager.getInstance(); } private final Map<RequestID, ReportListener> mRequestTable = new Hashtable<RequestID, ReportListener>(); /** * Instances of this class receive execution report from ORS and * emit them into data flows. */ private static class ReportListenerEmitter implements ReportListener { /** * Creates an instance. * * @param inSupport the data emitter support instance. */ public ReportListenerEmitter(DataEmitterSupport inSupport) { mSupport = inSupport; } @Override public void receiveExecutionReport(ExecutionReport inReport) { SLF4JLoggerProxy.debug(this, "Emitting Report {}", //$NON-NLS-1$ inReport); mSupport.send(inReport); } @Override public void receiveCancelReject(OrderCancelReject inReport) { SLF4JLoggerProxy.debug(this, "Emitting Cancel Reject {}", //$NON-NLS-1$ inReport); mSupport.send(inReport); } private final DataEmitterSupport mSupport; } }