package org.marketcetera.client.rpc;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.lang.Validate;
import org.marketcetera.client.Client;
import org.marketcetera.client.ClientImpl;
import org.marketcetera.client.ClientParameters;
import org.marketcetera.client.ClientVersion;
import org.marketcetera.client.ConnectionException;
import org.marketcetera.client.Messages;
import org.marketcetera.client.brokers.BrokerStatus;
import org.marketcetera.client.brokers.BrokersStatus;
import org.marketcetera.client.rpc.RpcClient.BrokersStatusRequest;
import org.marketcetera.client.rpc.RpcClient.BrokersStatusResponse;
import org.marketcetera.client.rpc.RpcClient.Locale;
import org.marketcetera.client.rpc.RpcClient.LoginRequest;
import org.marketcetera.client.rpc.RpcClient.LoginResponse;
import org.marketcetera.client.rpc.RpcClient.LogoutRequest;
import org.marketcetera.client.rpc.RpcClient.NextOrderIdRequest;
import org.marketcetera.client.rpc.RpcClient.NextOrderIdResponse;
import org.marketcetera.client.rpc.RpcClient.OpenOrdersRequest;
import org.marketcetera.client.rpc.RpcClient.OpenOrdersResponse;
import org.marketcetera.client.rpc.RpcClient.ReportsSinceRequest;
import org.marketcetera.client.rpc.RpcClient.ReportsSinceResponse;
import org.marketcetera.client.rpc.RpcClient.RpcClientService;
import org.marketcetera.client.rpc.RpcClient.RpcClientService.BlockingInterface;
import org.marketcetera.client.users.UserInfo;
import org.marketcetera.core.Util;
import org.marketcetera.core.position.PositionKey;
import org.marketcetera.core.position.PositionKeyFactory;
import org.marketcetera.trade.BrokerID;
import org.marketcetera.trade.Currency;
import org.marketcetera.trade.Equity;
import org.marketcetera.trade.ExecutionReportImpl;
import org.marketcetera.trade.FIXMessageWrapper;
import org.marketcetera.trade.Future;
import org.marketcetera.trade.Hierarchy;
import org.marketcetera.trade.Instrument;
import org.marketcetera.trade.Option;
import org.marketcetera.trade.OrderID;
import org.marketcetera.trade.ReportBase;
import org.marketcetera.trade.ReportBaseImpl;
import org.marketcetera.trade.UserID;
import org.marketcetera.util.except.I18NException;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.ws.tags.NodeId;
import org.marketcetera.util.ws.tags.SessionId;
import org.marketcetera.util.ws.wrappers.RemoteException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import com.googlecode.protobuf.pro.duplex.PeerInfo;
import com.googlecode.protobuf.pro.duplex.RpcClientChannel;
import com.googlecode.protobuf.pro.duplex.client.DuplexTcpClientPipelineFactory;
import com.googlecode.protobuf.pro.duplex.execute.RpcServerCallExecutor;
import com.googlecode.protobuf.pro.duplex.execute.ThreadPoolCallExecutor;
/* $License$ */
/**
* Provides an RPC implementation of {@link Client}.
*
* <p>This client replaces the web services component of the standard client,
* not the JMS component.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: RpcClientImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ThreadSafe
@ClassVersion("$Id: RpcClientImpl.java 16901 2014-05-11 16:14:11Z colin $")
public class RpcClientImpl
extends ClientImpl
{
/**
* Create a new RpcClientImpl instance.
*
* @param inParameters a <code>ClientParameters</code> value
* @throws ConnectionException if an error occurs connecting to the RPC client
*/
public RpcClientImpl(ClientParameters inParameters)
throws ConnectionException
{
super(inParameters);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getAllEquityPositionsAsOf(java.util.Date)
*/
@Override
public Map<PositionKey<Equity>,BigDecimal> getAllEquityPositionsAsOf(Date inDate)
throws ConnectionException
{
return getAllInstrumentPositionsAsOf(inDate,
RpcClient.InstrumentType.EQUITY);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getEquityPositionAsOf(java.util.Date, org.marketcetera.trade.Equity)
*/
@Override
public BigDecimal getEquityPositionAsOf(Date inDate,
Equity inEquity)
throws ConnectionException
{
return getInstrumentPositionAsOf(inDate,
inEquity);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getCurrencyPositionAsOf(java.util.Date, org.marketcetera.trade.Currency)
*/
@Override
public BigDecimal getCurrencyPositionAsOf(Date inDate,
Currency inCurrency)
throws ConnectionException
{
return getInstrumentPositionAsOf(inDate,
inCurrency);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getAllCurrencyPositionsAsOf(java.util.Date)
*/
@Override
public Map<PositionKey<Currency>,BigDecimal> getAllCurrencyPositionsAsOf(Date inDate)
throws ConnectionException
{
return getAllInstrumentPositionsAsOf(inDate,
RpcClient.InstrumentType.CURRENCY);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getAllFuturePositionsAsOf(java.util.Date)
*/
@Override
public Map<PositionKey<Future>,BigDecimal> getAllFuturePositionsAsOf(Date inDate)
throws ConnectionException
{
return getAllInstrumentPositionsAsOf(inDate,
RpcClient.InstrumentType.FUTURE);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getFuturePositionAsOf(java.util.Date, org.marketcetera.trade.Future)
*/
@Override
public BigDecimal getFuturePositionAsOf(Date inDate,
Future inFuture)
throws ConnectionException
{
return getInstrumentPositionAsOf(inDate,
inFuture);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getOptionPositionAsOf(java.util.Date, org.marketcetera.trade.Option)
*/
@Override
public BigDecimal getOptionPositionAsOf(Date inDate,
Option inOption)
throws ConnectionException
{
return getInstrumentPositionAsOf(inDate,
inOption);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getAllOptionPositionsAsOf(java.util.Date)
*/
@Override
public Map<PositionKey<Option>,BigDecimal> getAllOptionPositionsAsOf(Date inDate)
throws ConnectionException
{
return getAllInstrumentPositionsAsOf(inDate,
RpcClient.InstrumentType.OPTION);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getOptionPositionsAsOf(java.util.Date, java.lang.String[])
*/
@Override
public Map<PositionKey<Option>,BigDecimal> getOptionPositionsAsOf(Date inDate,
String... inSymbols)
throws ConnectionException
{
return getAllInstrumentPositionsAsOf(inDate,
inSymbols);
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getUnderlying(java.lang.String)
*/
@Override
public String getUnderlying(String inOptionRoot)
throws ConnectionException
{
RpcClient.UnderlyingRequest request = RpcClient.UnderlyingRequest.newBuilder().setSessionId(sessionId.getValue()).setSymbol(inOptionRoot).build();
try {
RpcClient.UnderlyingResponse response = clientService.getUnderlying(controller,
request);
return response.hasSymbol()?response.getSymbol():null;
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getOptionRoots(java.lang.String)
*/
@Override
public Collection<String> getOptionRoots(String inUnderlying)
throws ConnectionException
{
RpcClient.OptionRootsRequest request = RpcClient.OptionRootsRequest.newBuilder().setSessionId(sessionId.getValue()).setSymbol(inUnderlying).build();
try {
RpcClient.OptionRootsResponse response = clientService.getOptionRoots(controller,
request);
return response.getSymbolList();
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#resolveSymbol(java.lang.String)
*/
@Override
public Instrument resolveSymbol(String inSymbol)
throws ConnectionException
{
try {
SLF4JLoggerProxy.debug(this,
"Resolving {}",
inSymbol);
RpcClient.ResolveSymbolResponse response = clientService.resolveSymbol(controller,
RpcClient.ResolveSymbolRequest.newBuilder().setSessionId(sessionId.getValue()).setSymbol(inSymbol).build());
Instrument instrument = null;
if(response.hasInstrument()) {
synchronized(contextLock) {
Unmarshaller unmarshaller = getUnmarshaller();
instrument = (Instrument)unmarshaller.unmarshal(new StringReader(response.getInstrument().getPayload()));
}
SLF4JLoggerProxy.debug(this,
"Resolved {} to {}",
inSymbol,
instrument);
}
return instrument;
} catch (ServiceException | JAXBException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#findRootOrderIdFor(org.marketcetera.trade.OrderID)
*/
@Override
public OrderID findRootOrderIdFor(OrderID inOrderID)
{
RpcClient.RootOrderIdRequest request = RpcClient.RootOrderIdRequest.newBuilder().setSessionId(sessionId.getValue()).setOrderId(inOrderID.getValue()).build();
try {
return new OrderID(clientService.getRootOrderIdFor(controller,
request).getOrderId());
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#addReport(org.marketcetera.trade.FIXMessageWrapper, org.marketcetera.trade.BrokerID, org.marketcetera.trade.Hierarchy)
*/
@Override
public void addReport(FIXMessageWrapper inReport,
BrokerID inBrokerID,
Hierarchy inHierarchy)
throws ConnectionException
{
StringWriter output = new StringWriter();
try {
synchronized(contextLock) {
Marshaller marshaller = getMarshaller();
marshaller.marshal(inReport,
output);
}
RpcClient.AddReportRequest request = RpcClient.AddReportRequest.newBuilder()
.setSessionId(sessionId.getValue())
.setBrokerId(inBrokerID.getValue())
.setMessage(output.toString())
.setHierarchy(RpcClient.Hierarchy.valueOf(inHierarchy.name())).build();
SLF4JLoggerProxy.debug(this,
"AddReport request: {}",
request);
RpcClient.AddReportResponse response = clientService.addReport(controller,
request);
SLF4JLoggerProxy.debug(this,
"AddReport response: {}",
response);
} catch (JAXBException | ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#deleteReport(org.marketcetera.trade.ExecutionReportImpl)
*/
@Override
public void deleteReport(ExecutionReportImpl inReport)
throws ConnectionException
{
StringWriter output = new StringWriter();
try {
synchronized(contextLock) {
Marshaller marshaller = getMarshaller();
marshaller.marshal(inReport,
output);
}
RpcClient.DeleteReportRequest request = RpcClient.DeleteReportRequest.newBuilder()
.setMessage(output.toString()).build();
SLF4JLoggerProxy.debug(this,
"DeleteReport request: {}",
request);
RpcClient.DeleteReportResponse response = clientService.deleteReport(controller,
request);
SLF4JLoggerProxy.debug(this,
"DeleteReport response: {}",
response);
} catch (JAXBException | ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getUserData()
*/
@Override
public Properties getUserData()
throws ConnectionException
{
RpcClient.GetUserDataRequest request = RpcClient.GetUserDataRequest.newBuilder().setSessionId(sessionId.getValue()).build();
try {
RpcClient.GetUserDataResponse response = clientService.getUserData(controller,
request);
Properties userData;
if(response.hasUserData()) {
userData = Util.propertiesFromString(response.getUserData());
} else {
userData = new Properties();
}
return userData;
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#setUserData(java.util.Properties)
*/
@Override
public void setUserData(Properties inProperties)
throws ConnectionException
{
RpcClient.SetUserDataRequest.Builder requestBuilder = RpcClient.SetUserDataRequest.newBuilder().setSessionId(sessionId.getValue());
if(inProperties != null) {
requestBuilder.setUserData(Util.propertiesToString(inProperties));
}
try {
clientService.setUserData(controller,
requestBuilder.build());
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getUserInfo(org.marketcetera.trade.UserID, boolean)
*/
@Override
public UserInfo getUserInfo(UserID inId,
boolean inUseCache)
throws ConnectionException
{
RpcClient.UserInfoRequest request = RpcClient.UserInfoRequest.newBuilder().setSessionId(sessionId.getValue()).setId(inId.getValue()).build();
try {
RpcClient.UserInfoResponse response = clientService.getUserInfo(controller,
request);
String userData = response.getUserInfo().getUserdata();
Properties props = new Properties();
if(userData != null) {
props = Util.propertiesFromString(userData);
}
UserInfo userInfo = new UserInfo(response.getUserInfo().getName(),
new UserID(response.getUserInfo().getId()),
response.getUserInfo().getActive(),
response.getUserInfo().getSuperuser(),
props);
return userInfo;
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getReportsSince(java.util.Date)
*/
@Override
public ReportBase[] getReportsSince(Date inDate)
throws ConnectionException
{
SLF4JLoggerProxy.debug(this,
"Requesting reports since {}",
inDate);
ReportsSinceRequest request = RpcClient.ReportsSinceRequest.newBuilder().setSessionId(sessionId.getValue()).setOrigin(inDate.getTime()).build();
try {
ReportsSinceResponse response = clientService.getReportsSince(controller,
request);
List<ReportBase> reports = Lists.newArrayList();
for(String report : response.getReports().getReportsList()) {
synchronized(contextLock) {
reports.add((ReportBase)getUnmarshaller().unmarshal(new StringReader(report)));
}
}
SLF4JLoggerProxy.debug(this,
"Retrieved reports: {}",
reports);
return reports.toArray(new ReportBase[reports.size()]);
} catch (ServiceException | JAXBException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getOpenOrders()
*/
@Override
public List<ReportBaseImpl> getOpenOrders()
throws ConnectionException
{
SLF4JLoggerProxy.debug(this,
"Requesting open orders");
OpenOrdersRequest request = RpcClient.OpenOrdersRequest.newBuilder().setSessionId(sessionId.getValue()).build();
try {
OpenOrdersResponse response = clientService.getOpenOrders(controller,
request);
List<ReportBaseImpl> reports = Lists.newArrayList();
for(String report : response.getReports().getReportsList()) {
synchronized(contextLock) {
reports.add((ReportBaseImpl)getUnmarshaller().unmarshal(new StringReader(report)));
}
}
SLF4JLoggerProxy.debug(this,
"Retrieved open orders: {}",
reports);
return reports;
} catch (ServiceException | JAXBException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getBrokersStatus()
*/
@Override
public BrokersStatus getBrokersStatus()
throws ConnectionException
{
BrokersStatusRequest brokersStatusRequest = BrokersStatusRequest.newBuilder().setSessionId(sessionId.getValue()).build();
try {
BrokersStatusResponse brokersStatusResponse = clientService.getBrokersStatus(controller,
brokersStatusRequest);
RpcClient.BrokersStatus rpcBrokersStatus = brokersStatusResponse.getBrokersStatus();
List<BrokerStatus> brokers = Lists.newArrayList();
for(RpcClient.BrokerStatus rpcBrokerStatus : rpcBrokersStatus.getBrokersList()) {
BrokerStatus brokerStatus = new BrokerStatus(rpcBrokerStatus.getName(),
new BrokerID(rpcBrokerStatus.getBrokerId()),
rpcBrokerStatus.getLoggedOn());
brokers.add(brokerStatus);
}
BrokersStatus brokersStatus = new BrokersStatus(brokers);
return brokersStatus;
} catch (ServiceException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#close()
*/
@Override
public synchronized void close()
{
try {
super.close();
} catch (Exception ignored) {}
stopRpcServices();
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getNextServerID()
*/
@Override
protected String getNextServerID()
throws RemoteException
{
NextOrderIdRequest nextOrderIdRequest = NextOrderIdRequest.newBuilder().setSessionId(sessionId.getValue()).build();
try {
NextOrderIdResponse nextOrderIdResponse = clientService.getNextOrderID(controller,
nextOrderIdRequest);
return nextOrderIdResponse.getOrderId();
} catch (ServiceException e) {
throw new RemoteException(e);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#heartbeat()
*/
@Override
protected void heartbeat()
throws RemoteException
{
RpcClient.HeartbeatRequest request = RpcClient.HeartbeatRequest.newBuilder().setId(System.nanoTime()).build();
try {
clientService.heartbeat(controller,
request);
return;
} catch (Exception e) {
throw new RemoteException(e);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#getSessionId()
*/
@Override
protected SessionId getSessionId()
{
return sessionId;
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#connectWebServices()
*/
@Override
protected void connectWebServices()
throws I18NException, RemoteException
{
SLF4JLoggerProxy.debug(this,
"Connecting to RPC server at {}:{}",
mParameters.getHostname(),
mParameters.getPort());
PeerInfo server = new PeerInfo(mParameters.getHostname(),
mParameters.getPort());
DuplexTcpClientPipelineFactory clientFactory = new DuplexTcpClientPipelineFactory();
executor = new ThreadPoolCallExecutor(1,
10);
clientFactory.setRpcServerCallExecutor(executor);
clientFactory.setConnectResponseTimeoutMillis(10000);
clientFactory.setCompression(true);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup());
bootstrap.handler(clientFactory);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.TCP_NODELAY,
true);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
10000);
bootstrap.option(ChannelOption.SO_SNDBUF,
1048576);
bootstrap.option(ChannelOption.SO_RCVBUF,
1048576);
try {
channel = clientFactory.peerWith(server,
bootstrap);
clientService = RpcClientService.newBlockingStub(channel);
controller = channel.newRpcController();
java.util.Locale currentLocale = java.util.Locale.getDefault();
LoginRequest loginRequest = LoginRequest.newBuilder()
.setAppId(ClientVersion.APP_ID.getValue())
.setVersionId(ClientVersion.APP_ID_VERSION.getVersionInfo())
.setClientId(NodeId.generate().getValue())
.setLocale(Locale.newBuilder()
.setCountry(currentLocale.getCountry()==null?"":currentLocale.getCountry())
.setLanguage(currentLocale.getLanguage()==null?"":currentLocale.getLanguage())
.setVariant(currentLocale.getVariant()==null?"":currentLocale.getVariant()).build())
.setUsername(mParameters.getUsername())
.setPassword(new String(mParameters.getPassword())).build();
LoginResponse loginResponse = clientService.login(controller,
loginRequest);
sessionId = new SessionId(loginResponse.getSessionId());
} catch (IOException | ServiceException e) {
throw new RemoteException(e);
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#reconnectWebServices()
*/
@Override
protected void reconnectWebServices()
throws RemoteException
{
try {
stopRpcServices();
} catch (Exception ignored) {
}
connectWebServices();
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#closeWebServices()
*/
@Override
protected void closeWebServices()
throws RemoteException
{
stopRpcServices();
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#connectJms()
*/
@Override
protected void connectJms()
throws JAXBException
{
if(useJms()) {
super.connectJms();
}
}
/* (non-Javadoc)
* @see org.marketcetera.client.ClientImpl#startJms()
*/
@Override
protected void startJms()
throws JAXBException
{
if(useJms()) {
super.startJms();
}
}
/**
* Indicates if the client should active JMS or not.
*
* @return a <code>boolean</code> value
*/
private boolean useJms()
{
if(mParameters instanceof RpcClientParameters) {
RpcClientParameters rpcParms = (RpcClientParameters)mParameters;
return rpcParms.getUseJms();
}
return true;
}
/**
* Stops RPC services.
*/
private void stopRpcServices()
{
try {
try {
clientService.logout(controller,
LogoutRequest.newBuilder().setSessionId(sessionId.getValue()).build());
} catch (Exception ignored) {}
if(executor != null) {
try {
executor.shutdownNow();
} catch (Exception ignored) {}
}
if(channel != null) {
try {
channel.close();
} catch (Exception ignored) {}
}
} finally {
executor = null;
controller = null;
clientService = null;
channel = null;
sessionId = null;
}
}
/**
* Gets the position of the given instrument at the given point in time.
*
* @param inDate a <code>Date</code> value
* @param inInstrument an <code>InstrumentClazz</code> value
* @return a <code>BigDecimal</code> value
*/
private <InstrumentClazz extends Instrument> BigDecimal getInstrumentPositionAsOf(Date inDate,
InstrumentClazz inInstrument)
{
Map<PositionKey<InstrumentClazz>,BigDecimal> positionMap = getInstrumentPositionsAsOf(inDate,
null,
inInstrument);
if(positionMap == null || positionMap.isEmpty()) {
return BigDecimal.ZERO;
}
Validate.isTrue(positionMap.size() == 1);
return positionMap.values().iterator().next();
}
/**
* Gets the positions of all instruments of the given type at the given point in time.
*
* @param inDate a <code>Date</code> value
* @param inInstrumentType an <code>RpcClient.InstrumentType</code> value or <code>null</code>
* @return a <code>Map<PositionKey<InstrumentClazz>,BigDecimal></code> value
* @throws ConnectionException if an error occurs connecting to the server
*/
private <InstrumentClazz extends Instrument> Map<PositionKey<InstrumentClazz>,BigDecimal> getAllInstrumentPositionsAsOf(Date inDate,
RpcClient.InstrumentType inInstrumentType)
{
return getInstrumentPositionsAsOf(inDate,
inInstrumentType,
null);
}
/**
* Gets the positions of all options of the given roots at the given point in time.
*
* @param inDate a <code>Date</code> value
* @param inOptionRoots a <code>String...</code> value
* @return a <code>Map<PositionKey<InstrumentClazz>,BigDecimal></code> value
* @throws ConnectionException if an error occurs connecting to the server
*/
private <InstrumentClazz extends Instrument> Map<PositionKey<InstrumentClazz>,BigDecimal> getAllInstrumentPositionsAsOf(Date inDate,
String...inOptionRoots)
{
return getInstrumentPositionsAsOf(inDate,
null,
null,
inOptionRoots);
}
/**
* Gets the positions of instrument or instruments as described by the parameters.
*
* @param inDate a <code>Date</code> value
* @param inInstrumentType an <code>RpcClient.InstrumentType</code> value or <code>null</code>
* @param inInstrument an <code>Instrument</code> value or <code>null</code>
* @param inOptionRoots a <code>String...</code> value, may be empty
* @return a <code>Map<PositionKey<InstrumentClazz>,BigDecimal></code> value
* @throws ConnectionException if an error occurs connecting to the server
*/
@SuppressWarnings("unchecked")
private <InstrumentClazz extends Instrument> Map<PositionKey<InstrumentClazz>,BigDecimal> getInstrumentPositionsAsOf(Date inDate,
RpcClient.InstrumentType inInstrumentType,
Instrument inInstrument,
String...inOptionRoots)
{
SLF4JLoggerProxy.debug(this,
"Getting instrument position as of {} for {}/{}/{}",
inDate,
inInstrumentType,
inInstrument,
inOptionRoots == null?"":Arrays.toString(inOptionRoots));
try {
RpcClient.PositionRequest request;
if(inInstrument == null) {
if(inInstrumentType == null) {
RpcClient.PositionRequest.Builder requestBuilder = RpcClient.PositionRequest.newBuilder().setSessionId(sessionId.getValue()).setOrigin(inDate.getTime());
for(String optionRoot : inOptionRoots) {
requestBuilder.addRoot(optionRoot);
}
request = requestBuilder.build();
} else {
request = RpcClient.PositionRequest.newBuilder().setSessionId(sessionId.getValue()).setInstrumentType(inInstrumentType).setOrigin(inDate.getTime()).build();
}
} else {
StringWriter output = new StringWriter();
try {
synchronized(contextLock) {
getMarshaller().marshal(inInstrument,
output);
}
} catch (JAXBException e) {
throw new ServiceException(e);
}
request = RpcClient.PositionRequest.newBuilder()
.setSessionId(sessionId.getValue())
.setInstrument(RpcClient.Instrument.newBuilder().setPayload(output.toString()))
.setOrigin(inDate.getTime()).build();
}
RpcClient.PositionResponse response = clientService.getPositions(controller,
request);
Map<PositionKey<InstrumentClazz>,BigDecimal> positions = Maps.newHashMap();
Validate.isTrue(response.getKeysCount() == response.getValuesCount());
for(int index=0;index<response.getKeysCount();index++) {
RpcClient.PositionKey rpcKey = response.getKeys(index);
RpcClient.Instrument rpcInstrument = rpcKey.getInstrument();
String rpcAccount = rpcKey.getAccount();
String rpcTraderId = rpcKey.getTraderId();
Instrument instrument;
synchronized(contextLock) {
instrument = (Instrument)getUnmarshaller().unmarshal(new StringReader(rpcInstrument.getPayload()));
}
PositionKey<? extends Instrument> positionKey = null;
if(instrument instanceof Equity) {
positionKey = PositionKeyFactory.createEquityKey(instrument.getSymbol(),
rpcAccount,
rpcTraderId);
} else if(instrument instanceof Option) {
Option option = (Option)instrument;
positionKey = PositionKeyFactory.createOptionKey(option.getSymbol(),
option.getExpiry(),
option.getStrikePrice(),
option.getType(),
rpcAccount,
rpcTraderId);
} else if(instrument instanceof Future) {
Future future = (Future)instrument;
positionKey = PositionKeyFactory.createFutureKey(future.getSymbol(),
future.getExpiryAsString(),
rpcAccount,
rpcTraderId);
} else if(instrument instanceof Currency) {
Currency currency = (Currency)instrument;
positionKey = PositionKeyFactory.createCurrencyKey(currency.getSymbol(),
rpcAccount,
rpcTraderId);
} else {
throw new UnsupportedOperationException();
}
positions.put((PositionKey<InstrumentClazz>)positionKey,
new BigDecimal(response.getValues(index)));
}
SLF4JLoggerProxy.debug(this,
"Returning positions: {}",
positions);
return positions;
} catch (ServiceException | JAXBException e) {
throw new ConnectionException(e,
Messages.ERROR_REMOTE_EXECUTION);
}
}
/**
* Gets the list of context classes to use with the JAXB context.
*
* @return a <code>Class<?>[]</code> value
*/
private Class<?>[] getContextClasses()
{
if(mParameters instanceof RpcClientParameters) {
RpcClientParameters params = (RpcClientParameters)mParameters;
if(params.getContextClassProvider() != null) {
return params.getContextClassProvider().getContextClasses();
}
}
return new Class<?>[0];
}
/**
* Gets the context marshaller.
*
* @return a <code>Marshaller</code> value
* @throws JAXBException if an error occurs creating the marshaller
*/
private Marshaller getMarshaller()
throws JAXBException
{
synchronized(contextLock) {
if(context == null) {
initContext();
}
return marshaller;
}
}
/**
* Gets the context unmarshaller.
*
* @return an <code>Unmarshaller</code> value
* @throws JAXBException if an error occurs creating the unmarshaller
*/
private Unmarshaller getUnmarshaller()
throws JAXBException
{
synchronized(contextLock) {
if(context == null) {
initContext();
}
return unmarshaller;
}
}
/**
* Initializes the marshalling/unmarshalling context.
*
* @throws JAXBException if an error occurs creating the unmarshaller
*/
private void initContext()
throws JAXBException
{
synchronized(contextLock) {
context = JAXBContext.newInstance(getContextClasses());
marshaller = context.createMarshaller();
unmarshaller = context.createUnmarshaller();
}
}
/**
* controller responsible for the RPC connection
*/
private RpcController controller;
/**
* session ID value for this connection, may be <code>null</code> if the connection is inactive
*/
private SessionId sessionId;
/**
* provides access to RPC services
*/
private BlockingInterface clientService;
/**
* executes the nitty-gritty of the calls
*/
private RpcServerCallExecutor executor;
/**
* channel over which calls are made
*/
private RpcClientChannel channel;
/**
* guards access to JAXB context objects
*/
private final Object contextLock = new Object();
/**
* context used to serialize and unserialize messages as necessary
*/
@GuardedBy("contextLock")
private JAXBContext context;
/**
* marshals messages
*/
@GuardedBy("contextLock")
private Marshaller marshaller;
/**
* unmarshals messages
*/
@GuardedBy("contextLock")
private Unmarshaller unmarshaller;
}