/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.position; import static org.apache.commons.lang.StringEscapeUtils.escapeJavaScript; import static org.apache.commons.lang.StringUtils.defaultString; import static org.apache.commons.lang.StringUtils.replace; import static org.apache.commons.lang.StringUtils.trimToEmpty; import static org.apache.commons.lang.StringUtils.trimToNull; import java.math.BigDecimal; import java.net.URI; import java.util.Collection; import java.util.Collections; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.apache.commons.lang.math.NumberUtils; import org.joda.beans.Bean; import org.joda.beans.impl.flexi.FlexiBean; import org.threeten.bp.format.DateTimeFormatter; import com.google.common.base.Objects; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource; import com.opengamma.core.security.Security; import com.opengamma.core.value.MarketDataRequirementNames; 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.PositionMaster; import com.opengamma.util.JodaBeanSerialization; /** * RESTful resource for a position. */ @Path("/positions/{positionId}") public class WebPositionResource extends AbstractWebPositionResource { /** * Creates the resource. * @param parent the parent resource, not null */ public WebPositionResource(final AbstractWebPositionResource parent) { super(parent); } //------------------------------------------------------------------------- @GET @Produces(MediaType.TEXT_HTML) public String getHTML() { FlexiBean out = createRootData(); return getFreemarker().build(HTML_DIR + "position.ftl", out); } @GET @Produces(MediaType.APPLICATION_JSON) public String getJSON() { FlexiBean out = createRootData(); return getFreemarker().build(JSON_DIR + "position.ftl", out); } //------------------------------------------------------------------------- @PUT @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.TEXT_HTML) public Response putHTML(@FormParam(POSITION_XML) String positionXml) { PositionDocument doc = data().getPosition(); if (doc.isLatest() == false) { return Response.status(Status.FORBIDDEN).entity(getHTML()).build(); } positionXml = trimToNull(positionXml); if (positionXml == null) { FlexiBean out = createRootData(); out.put("err_xmlMissing", true); out.put(POSITION_XML, defaultString(positionXml)); String html = getFreemarker().build(HTML_DIR + "position-update.ftl", out); return Response.ok(html).build(); } URI uri = updatePosition(positionXml); return Response.seeOther(uri).build(); } private URI updatePosition(String positionXml) { Bean positionBean = JodaBeanSerialization.deserializer().xmlReader().read(positionXml); PositionMaster positionMaster = data().getPositionMaster(); positionMaster.update(new PositionDocument((ManageablePosition) positionBean)); return WebPositionResource.uri(data()); } @PUT @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) public Response putJSON(@FormParam("quantity") String quantityStr, @FormParam("tradesJson") String tradesJson, @FormParam("type") String type, @FormParam(POSITION_XML) String positionXml) { PositionDocument doc = data().getPosition(); if (doc.isLatest() == false) { return Response.status(Status.FORBIDDEN).entity(getHTML()).build(); } type = defaultString(trimToNull(type)); switch (type) { case "xml": updatePosition(trimToEmpty(positionXml)); break; default: quantityStr = replace(trimToNull(quantityStr), ",", ""); tradesJson = trimToNull(tradesJson); Collection<ManageableTrade> trades = null; if (tradesJson != null) { trades = parseTrades(tradesJson); } else { trades = Collections.<ManageableTrade>emptyList(); } BigDecimal quantity = quantityStr != null && NumberUtils.isNumber(quantityStr) ? new BigDecimal(quantityStr) : null; updatePosition(doc, quantity, trades); } return Response.ok().build(); } private URI updatePosition(PositionDocument doc, BigDecimal quantity, Collection<ManageableTrade> trades) { ManageablePosition position = doc.getPosition(); if (Objects.equal(position.getQuantity(), quantity) == false || trades != null) { position.setQuantity(quantity); if (trades != null) { position.getTrades().clear(); for (ManageableTrade trade : trades) { trade.setSecurityLink(position.getSecurityLink()); position.addTrade(trade); } } PositionDocument updated = data().getPositionMaster().update(doc); data().setPosition(updated); } return WebPositionResource.uri(data()); } //------------------------------------------------------------------------- @DELETE @Produces(MediaType.TEXT_HTML) public Response deleteHTML() { PositionDocument doc = data().getPosition(); if (doc.isLatest() == false) { return Response.status(Status.FORBIDDEN).entity(getHTML()).build(); } URI uri = deletePosition(doc); return Response.seeOther(uri).build(); } @DELETE @Produces(MediaType.APPLICATION_JSON) public Response deleteJSON() { PositionDocument doc = data().getPosition(); if (doc.isLatest()) { deletePosition(doc); } return Response.ok().build(); } private URI deletePosition(PositionDocument doc) { data().getPositionMaster().remove(doc.getUniqueId()); return WebPositionResource.uri(data()); } //------------------------------------------------------------------------- /** * Creates the output root data. * @return the output root data, not null */ @Override protected FlexiBean createRootData() { FlexiBean out = super.createRootData(); out.put("timeFormatterJson", DateTimeFormatter.ofPattern("HH:mm:ss")); out.put("offsetFormatterJson", DateTimeFormatter.ofPattern("Z")); PositionDocument doc = data().getPosition(); // REVIEW jonathan 2012-01-12 -- we are throwing away any adjuster that may be required, e.g. to apply // normalisation to the time-series. This reproduces the previous behaviour but probably indicates that the // time-series information is in the wrong place. ObjectId tsObjectId = null; Security security = doc.getPosition().getSecurityLink().resolveQuiet(data().getSecuritySource()); if (security != null && !security.getExternalIdBundle().isEmpty()) { // Get the last price HTS for the security HistoricalTimeSeriesSource htsSource = data().getHistoricalTimeSeriesSource(); HistoricalTimeSeries series = htsSource.getHistoricalTimeSeries( MarketDataRequirementNames.MARKET_VALUE, doc.getPosition().getSecurity().getExternalIdBundle(), null, null, false, null, false, 0); if (series != null) { tsObjectId = series.getUniqueId().getObjectId(); } } out.put("positionDoc", doc); out.put("position", doc.getPosition()); out.put("security", doc.getPosition().getSecurity()); out.put("timeSeriesId", tsObjectId); out.put("deleted", !doc.isLatest()); out.put("attributes", doc.getPosition().getAttributes()); out.put("tradeAttrModel", getTradeAttributesModel()); out.put(POSITION_XML, escapeJavaScript(defaultString(getPositionXml(doc.getPosition())))); return out; } private TradeAttributesModel getTradeAttributesModel() { PositionDocument doc = data().getPosition(); TradeAttributesModel getTradeAttributesModel = new TradeAttributesModel(doc.getPosition()); return getTradeAttributesModel; } //------------------------------------------------------------------------- @Path("versions") public WebPositionVersionsResource findVersions() { return new WebPositionVersionsResource(this); } //------------------------------------------------------------------------- /** * Builds a URI for this resource. * @param data the data, not null * @return the URI, not null */ public static URI uri(final WebPositionsData data) { return uri(data, null); } /** * Builds a URI for this resource. * @param data the data, not null * @param overridePositionId the override position id, null uses information from data * @return the URI, not null */ public static URI uri(final WebPositionsData data, final UniqueId overridePositionId) { String positionId = data.getBestPositionUriId(overridePositionId); return data.getUriInfo().getBaseUriBuilder().path(WebPositionResource.class).build(positionId); } }