/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.position;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.joda.beans.Bean;
import org.joda.beans.impl.flexi.FlexiBean;
import com.opengamma.DataNotFoundException;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.ExternalScheme;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueId;
import com.opengamma.master.position.ManageablePosition;
import com.opengamma.master.position.ManageableTrade;
import com.opengamma.master.position.PositionDocument;
import com.opengamma.master.position.PositionHistoryRequest;
import com.opengamma.master.position.PositionHistoryResult;
import com.opengamma.master.position.PositionMaster;
import com.opengamma.master.position.PositionSearchRequest;
import com.opengamma.master.position.PositionSearchResult;
import com.opengamma.master.position.impl.DelegatingPositionMaster;
import com.opengamma.master.security.ManageableSecurityLink;
import com.opengamma.master.security.SecurityLoader;
import com.opengamma.util.JodaBeanSerialization;
import com.opengamma.util.paging.PagingRequest;
import com.opengamma.web.WebPaging;
import com.opengamma.web.analytics.rest.MasterType;
import com.opengamma.web.analytics.rest.Subscribe;
import com.opengamma.web.analytics.rest.SubscribeMaster;
/**
* RESTful resource for all positions.
* <p>
* The positions resource represents the whole of a position master.
*/
@Path("/positions")
public class WebPositionsResource extends AbstractWebPositionResource {
/**
* Creates the resource.
* @param positionMaster the position master, not null
* @param securityLoader the security loader, not null
* @param securitySource the security source, not null
* @param htsSource the historical time series source, not null
* @param externalSchemes the map of external schemes, with {@link ExternalScheme} as key and description as value
*/
public WebPositionsResource(final PositionMaster positionMaster, final SecurityLoader securityLoader, final SecuritySource securitySource,
final HistoricalTimeSeriesSource htsSource, final Map<ExternalScheme, String> externalSchemes) {
super(positionMaster, securityLoader, securitySource, htsSource, externalSchemes);
}
//-------------------------------------------------------------------------
@GET
@Produces(MediaType.TEXT_HTML)
@SubscribeMaster(MasterType.POSITION)
public String getHTML(
@QueryParam("pgIdx") Integer pgIdx,
@QueryParam("pgNum") Integer pgNum,
@QueryParam("pgSze") Integer pgSze,
@QueryParam("identifier") String identifier,
@QueryParam("minquantity") String minQuantityStr,
@QueryParam("maxquantity") String maxQuantityStr,
@QueryParam("positionId") List<String> positionIdStrs,
@QueryParam("tradeId") List<String> tradeIdStrs,
@QueryParam("uniqueIdScheme") String uniqueIdScheme) {
PagingRequest pr = buildPagingRequest(pgIdx, pgNum, pgSze);
FlexiBean out = createSearchResultData(pr, identifier, minQuantityStr, maxQuantityStr, positionIdStrs, tradeIdStrs, uniqueIdScheme);
return getFreemarker().build(HTML_DIR + "positions.ftl", out);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@SubscribeMaster(MasterType.POSITION)
public String getJSON(
@QueryParam("pgIdx") Integer pgIdx,
@QueryParam("pgNum") Integer pgNum,
@QueryParam("pgSze") Integer pgSze,
@QueryParam("identifier") String identifier,
@QueryParam("minquantity") String minQuantityStr,
@QueryParam("maxquantity") String maxQuantityStr,
@QueryParam("positionId") List<String> positionIdStrs,
@QueryParam("tradeId") List<String> tradeIdStrs,
@QueryParam("uniqueIdScheme") String uniqueIdScheme) {
PagingRequest pr = buildPagingRequest(pgIdx, pgNum, pgSze);
FlexiBean out = createSearchResultData(pr, identifier, minQuantityStr, maxQuantityStr, positionIdStrs, tradeIdStrs, uniqueIdScheme);
return getFreemarker().build(JSON_DIR + "positions.ftl", out);
}
private FlexiBean createSearchResultData(PagingRequest pr, String identifier, String minQuantityStr,
String maxQuantityStr, List<String> positionIdStrs, List<String> tradeIdStrs, String uniqueIdScheme) {
minQuantityStr = StringUtils.defaultString(minQuantityStr).replace(",", "");
maxQuantityStr = StringUtils.defaultString(maxQuantityStr).replace(",", "");
FlexiBean out = createRootData();
PositionSearchRequest searchRequest = new PositionSearchRequest();
searchRequest.setPagingRequest(pr);
searchRequest.setSecurityIdValue(StringUtils.trimToNull(identifier));
searchRequest.setUniqueIdScheme(StringUtils.trimToNull(uniqueIdScheme));
if (NumberUtils.isNumber(minQuantityStr)) {
searchRequest.setMinQuantity(NumberUtils.createBigDecimal(minQuantityStr));
}
if (NumberUtils.isNumber(maxQuantityStr)) {
searchRequest.setMaxQuantity(NumberUtils.createBigDecimal(maxQuantityStr));
}
for (String positionIdStr : positionIdStrs) {
searchRequest.addPositionObjectId(ObjectId.parse(positionIdStr));
}
for (String tradeIdStr : tradeIdStrs) {
searchRequest.addPositionObjectId(ObjectId.parse(tradeIdStr));
}
out.put("searchRequest", searchRequest);
if (data().getUriInfo().getQueryParameters().size() > 0) {
PositionSearchResult searchResult = data().getPositionMaster().search(searchRequest);
out.put("searchResult", searchResult);
out.put("paging", new WebPaging(searchResult.getPaging(), data().getUriInfo()));
}
return out;
}
//-------------------------------------------------------------------------
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
public Response postHTML(
@FormParam("quantity") String quantityStr,
@FormParam("idscheme") String idScheme,
@FormParam("idvalue") String idValue,
@FormParam("type") String type,
@FormParam(POSITION_XML) String positionXml,
@FormParam("uniqueIdScheme") String uniqueIdScheme) {
uniqueIdScheme = StringUtils.trimToNull(uniqueIdScheme);
type = StringUtils.trimToEmpty(type);
URI uri = null;
switch (type) {
case "xml":
positionXml = StringUtils.trimToNull(positionXml);
if (positionXml == null) {
FlexiBean out = createRootData();
out.put("err_xmlMissing", true);
String html = getFreemarker().build(HTML_DIR + "positions-add.ftl", out);
return Response.ok(html).build();
}
uri = addPosition(positionXml, uniqueIdScheme);
break;
case StringUtils.EMPTY:
quantityStr = StringUtils.replace(StringUtils.trimToNull(quantityStr), ",", "");
BigDecimal quantity = quantityStr != null && NumberUtils.isNumber(quantityStr) ? new BigDecimal(quantityStr) : null;
idScheme = StringUtils.trimToNull(idScheme);
idValue = StringUtils.trimToNull(idValue);
if (quantity == null || idScheme == null || idValue == null) {
FlexiBean out = createRootData();
if (quantityStr == null) {
out.put("err_quantityMissing", true);
}
if (quantity == null) {
out.put("err_quantityNotNumeric", true);
}
if (idScheme == null) {
out.put("err_idschemeMissing", true);
}
if (idValue == null) {
out.put("err_idvalueMissing", true);
}
out.put("quantity", quantityStr);
out.put("idvalue", idValue);
String html = getFreemarker().build(HTML_DIR + "positions-add.ftl", out);
return Response.ok(html).build();
}
ExternalIdBundle id = ExternalIdBundle.of(ExternalId.of(idScheme, idValue));
UniqueId secUid = getSecurityUniqueId(id);
if (secUid == null) {
FlexiBean out = createRootData();
out.put("err_idvalueNotFound", true);
String html = getFreemarker().build(HTML_DIR + "positions-add.ftl", out);
return Response.ok(html).build();
}
uri = addPosition(quantity, secUid, uniqueIdScheme);
break;
default:
throw new IllegalArgumentException("Can only add position by XML or completing provided web form");
}
return Response.seeOther(uri).build();
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response postJSON(
@FormParam("quantity") String quantityStr,
@FormParam("idscheme") String idScheme,
@FormParam("idvalue") String idValue,
@FormParam("tradesJson") String tradesJson,
@FormParam("type") String type,
@FormParam(POSITION_XML) String positionXml,
@FormParam("uniqueIdScheme") String uniqueIdScheme) {
uniqueIdScheme = StringUtils.trimToNull(uniqueIdScheme);
type = StringUtils.trimToEmpty(type);
URI uri = null;
switch (type) {
case "xml":
uri = addPosition(positionXml, uniqueIdScheme);
break;
case StringUtils.EMPTY:
quantityStr = StringUtils.replace(StringUtils.trimToNull(quantityStr), ",", "");
BigDecimal quantity = quantityStr != null && NumberUtils.isNumber(quantityStr) ? new BigDecimal(quantityStr) : null;
idScheme = StringUtils.trimToNull(idScheme);
idValue = StringUtils.trimToNull(idValue);
tradesJson = StringUtils.trimToNull(tradesJson);
if (quantity == null || idScheme == null || idValue == null) {
return Response.status(Status.BAD_REQUEST).build();
}
ExternalIdBundle id = ExternalIdBundle.of(ExternalId.of(idScheme, idValue));
UniqueId secUid = getSecurityUniqueId(id);
if (secUid == null) {
throw new DataNotFoundException("invalid " + idScheme + "~" + idValue);
}
Collection<ManageableTrade> trades = null;
if (tradesJson != null) {
trades = parseTrades(tradesJson);
} else {
trades = Collections.<ManageableTrade>emptyList();
}
uri = addPosition(quantity, secUid, trades, uniqueIdScheme);
break;
default:
throw new IllegalArgumentException("Can only add position by XML or completing provided web form");
}
return Response.created(uri).build();
}
private URI addPosition(String positionXml, String uniqueIdScheme) {
positionXml = StringUtils.trimToEmpty(positionXml);
Bean positionBean = JodaBeanSerialization.deserializer().xmlReader().read(positionXml);
PositionMaster positionMaster = data().getPositionMaster();
ManageablePosition manageablePosition = (ManageablePosition) positionBean;
if (uniqueIdScheme != null) {
manageablePosition.setUniqueId(UniqueId.of(uniqueIdScheme, uniqueIdScheme));
}
ManageablePosition position = positionMaster.add(new PositionDocument(manageablePosition)).getPosition();
return new WebPositionsUris(data()).position(position);
}
private UniqueId getSecurityUniqueId(ExternalIdBundle id) {
UniqueId result = null;
Security security = data().getSecuritySource().getSingle(id);
if (security != null) {
result = security.getUniqueId();
} else {
result = data().getSecurityLoader().loadSecurity(id);
}
return result;
}
private URI addPosition(BigDecimal quantity, UniqueId secUid, String uniqueIdScheme) {
return addPosition(quantity, secUid, Collections.<ManageableTrade>emptyList(), uniqueIdScheme);
}
private URI addPosition(BigDecimal quantity, UniqueId secUid, Collection<ManageableTrade> trades, String uniqueIdScheme) {
ExternalIdBundle secId = data().getSecuritySource().get(secUid).getExternalIdBundle();
ManageablePosition position = new ManageablePosition(quantity, secId);
if (uniqueIdScheme != null) {
position.setUniqueId(UniqueId.of(uniqueIdScheme, uniqueIdScheme));
}
for (ManageableTrade trade : trades) {
trade.setSecurityLink(new ManageableSecurityLink(secId));
position.addTrade(trade);
}
PositionDocument doc = new PositionDocument(position);
doc = data().getPositionMaster().add(doc);
data().setPosition(doc);
return WebPositionResource.uri(data());
}
//-------------------------------------------------------------------------
@Path("{positionId}")
public WebPositionResource findPosition(@Subscribe @PathParam("positionId") String idStr) {
data().setUriPositionId(idStr);
UniqueId oid = UniqueId.parse(idStr);
try {
PositionDocument doc = data().getPositionMaster().get(oid);
data().setPosition(doc);
} catch (DataNotFoundException ex) {
PositionHistoryRequest historyRequest = new PositionHistoryRequest(oid);
historyRequest.setPagingRequest(PagingRequest.ONE);
PositionHistoryResult historyResult = data().getPositionMaster().history(historyRequest);
if (historyResult.getDocuments().size() == 0) {
throw ex;
}
data().setPosition(historyResult.getFirstDocument());
}
return new WebPositionResource(this);
}
//-------------------------------------------------------------------------
/**
* Creates the output root data.
* @return the output root data, not null
*/
@Override
protected FlexiBean createRootData() {
FlexiBean out = super.createRootData();
PositionSearchRequest searchRequest = new PositionSearchRequest();
out.put("searchRequest", searchRequest);
if (data().getPositionMaster() instanceof DelegatingPositionMaster) {
DelegatingPositionMaster delegatingPositionMaster = (DelegatingPositionMaster) data().getPositionMaster();
Map<String, PositionMaster> delegates = delegatingPositionMaster.getDelegates();
out.put("uniqueIdSchemes", delegates.keySet());
}
return out;
}
//-------------------------------------------------------------------------
/**
* Builds a URI for positions.
* @param data the data, not null
* @return the URI, not null
*/
public static URI uri(WebPositionsData data) {
return data.getUriInfo().getBaseUriBuilder().path(WebPositionsResource.class).build();
}
}