/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.security;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.beans.Bean;
import org.joda.beans.impl.flexi.FlexiBean;
import com.opengamma.DataNotFoundException;
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.historicaltimeseries.HistoricalTimeSeriesMaster;
import com.opengamma.master.legalentity.LegalEntityMaster;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.master.security.SecurityDocument;
import com.opengamma.master.security.SecurityHistoryRequest;
import com.opengamma.master.security.SecurityHistoryResult;
import com.opengamma.master.security.SecurityLoader;
import com.opengamma.master.security.SecurityLoaderRequest;
import com.opengamma.master.security.SecurityLoaderResult;
import com.opengamma.master.security.SecurityMaster;
import com.opengamma.master.security.SecurityMetaDataRequest;
import com.opengamma.master.security.SecurityMetaDataResult;
import com.opengamma.master.security.SecuritySearchRequest;
import com.opengamma.master.security.SecuritySearchResult;
import com.opengamma.master.security.SecuritySearchSortOrder;
import com.opengamma.master.security.impl.DelegatingSecurityMaster;
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;
import com.sun.jersey.api.client.ClientResponse.Status;
/**
* RESTful resource for all securities.
* <p>
* The securities resource represents the whole of a security master.
*/
@Path("/securities")
public class WebSecuritiesResource extends AbstractWebSecurityResource {
/**
* Creates the resource.
* @param securityMaster the security master, not null
* @param securityLoader the security loader, not null
* @param htsMaster the historical time series master, not null
* @param legalEntityMaster the organization master, not null
*/
public WebSecuritiesResource(
final SecurityMaster securityMaster, final SecurityLoader securityLoader, final HistoricalTimeSeriesMaster htsMaster, final LegalEntityMaster legalEntityMaster) {
super(securityMaster, securityLoader, htsMaster, legalEntityMaster);
}
//-------------------------------------------------------------------------
@GET
@Produces(MediaType.TEXT_HTML)
@SubscribeMaster(MasterType.SECURITY)
public String getHTML(
@QueryParam("pgIdx") Integer pgIdx,
@QueryParam("pgNum") Integer pgNum,
@QueryParam("pgSze") Integer pgSze,
@QueryParam("sort") String sort,
@QueryParam("name") String name,
@QueryParam("identifier") String identifier,
@QueryParam("type") String type,
@QueryParam("uniqueIdScheme") String uniqueIdScheme,
@QueryParam("securityId") List<String> securityIdStrs,
@Context UriInfo uriInfo) {
PagingRequest pr = buildPagingRequest(pgIdx, pgNum, pgSze);
SecuritySearchSortOrder so = buildSortOrder(sort, SecuritySearchSortOrder.NAME_ASC);
FlexiBean out = createSearchResultData(pr, so, name, identifier, type, uniqueIdScheme, securityIdStrs, uriInfo);
return getFreemarker().build(HTML_DIR + "securities.ftl", out);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@SubscribeMaster(MasterType.SECURITY)
public String getJSON(
@QueryParam("pgIdx") Integer pgIdx,
@QueryParam("pgNum") Integer pgNum,
@QueryParam("pgSze") Integer pgSze,
@QueryParam("sort") String sort,
@QueryParam("name") String name,
@QueryParam("identifier") String identifier,
@QueryParam("type") String type,
@QueryParam("uniqueIdScheme") String uniqueIdScheme,
@QueryParam("securityId") List<String> securityIdStrs,
@Context UriInfo uriInfo) {
PagingRequest pr = buildPagingRequest(pgIdx, pgNum, pgSze);
SecuritySearchSortOrder so = buildSortOrder(sort, SecuritySearchSortOrder.NAME_ASC);
FlexiBean out = createSearchResultData(pr, so, name, identifier, type, uniqueIdScheme, securityIdStrs, uriInfo);
return getFreemarker().build(JSON_DIR + "securities.ftl", out);
}
private FlexiBean createSearchResultData(PagingRequest pr, SecuritySearchSortOrder so, String name, String identifier,
String type, String uniqueIdScheme, List<String> securityIdStrs, UriInfo uriInfo) {
FlexiBean out = createRootData();
SecuritySearchRequest searchRequest = new SecuritySearchRequest();
searchRequest.setPagingRequest(pr);
searchRequest.setSortOrder(so);
searchRequest.setName(StringUtils.trimToNull(name));
searchRequest.setExternalIdValue(StringUtils.trimToNull(identifier));
searchRequest.setSecurityType(StringUtils.trimToNull(type));
searchRequest.setUniqueIdScheme(StringUtils.trimToNull(uniqueIdScheme));
for (String securityIdStr : securityIdStrs) {
searchRequest.addObjectId(ObjectId.parse(securityIdStr));
}
MultivaluedMap<String, String> query = uriInfo.getQueryParameters();
for (int i = 0; query.containsKey("idscheme." + i) && query.containsKey("idvalue." + i); i++) {
ExternalId id = ExternalId.of(query.getFirst("idscheme." + i), query.getFirst("idvalue." + i));
searchRequest.addExternalId(id);
}
out.put("searchRequest", searchRequest);
if (data().getUriInfo().getQueryParameters().size() > 0) {
SecuritySearchResult searchResult = data().getSecurityMaster().search(searchRequest);
out.put("searchResult", searchResult);
out.put("paging", new WebPaging(searchResult.getPaging(), uriInfo));
}
return out;
}
//-------------------------------------------------------------------------
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
public Response postHTML(
@FormParam("type") String type,
@FormParam("idscheme") String idScheme,
@FormParam("idvalue") String idValue,
@FormParam(SECURITY_XML) String securityXml,
@FormParam("uniqueIdScheme") String uniqueIdScheme) {
uniqueIdScheme = StringUtils.trimToNull(uniqueIdScheme);
type = StringUtils.defaultString(StringUtils.trimToNull(type));
FlexiBean out = createRootData();
URI responseURI = null;
switch (type) {
case "xml":
boolean isValidInput = true;
try {
securityXml = StringUtils.trimToNull(securityXml);
if (securityXml == null) {
out.put("err_securityXmlMissing", true);
isValidInput = false;
}
if (uniqueIdScheme == null) {
out.put("err_unqiueIdSchemeMissing", true);
isValidInput = false;
}
if (!isValidInput) {
out.put(SECURITY_XML, StringEscapeUtils.escapeJavaScript(StringUtils.defaultString(securityXml)));
out.put("selectedUniqueIdScheme", StringUtils.defaultString(uniqueIdScheme));
return Response.ok(buildResponseHtml(out, "securities-add.ftl")).build();
}
ManageableSecurity security = addSecurity(securityXml, uniqueIdScheme);
WebSecuritiesUris webSecuritiesUris = new WebSecuritiesUris(data());
responseURI = webSecuritiesUris.security(security);
} catch (Exception ex) {
out.put("err_securityXmlMsg", ex.getMessage());
out.put(SECURITY_XML, StringEscapeUtils.escapeJavaScript(StringUtils.defaultString(securityXml)));
out.put("selectedUniqueIdScheme", StringUtils.defaultString(uniqueIdScheme));
return Response.ok(buildResponseHtml(out, "securities-add.ftl")).build();
}
break;
case "id":
idScheme = StringUtils.trimToNull(idScheme);
idValue = StringUtils.trimToNull(idValue);
if (idScheme == null || idValue == null) {
if (idScheme == null) {
out.put("err_idschemeMissing", true);
}
if (idValue == null) {
out.put("err_idvalueMissing", true);
}
out.put("idscheme", idScheme);
out.put("idvalue", idValue);
return Response.ok(buildResponseHtml(out, "securities-add.ftl")).build();
}
ExternalScheme scheme = ExternalScheme.of(idScheme);
Collection<ExternalIdBundle> bundles = buildSecurityRequest(scheme, idValue);
SecurityLoaderResult loaderResult = data().getSecurityLoader().loadSecurities(SecurityLoaderRequest.create(bundles));
Map<ExternalIdBundle, UniqueId> loadedSecurities = loaderResult.getResultMap();
if (bundles.size() == 1 && loadedSecurities.size() == 1) {
ExternalIdBundle identifierBundle = bundles.iterator().next();
responseURI = data().getUriInfo().getAbsolutePathBuilder().path(loadedSecurities.get(identifierBundle).toLatest().toString()).build();
} else {
responseURI = uri(data(), buildRequestAsExternalIdBundle(scheme, bundles));
}
break;
default:
throw new IllegalArgumentException("Can only add security by XML or ID");
}
return Response.seeOther(responseURI).build();
}
private String buildResponseHtml(FlexiBean out, String templateName) {
return getFreemarker().build(HTML_DIR + "securities-add.ftl", out);
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response postJSON(
@FormParam("type") String type,
@FormParam("idscheme") String idScheme,
@FormParam("idvalue") String idValue,
@FormParam(SECURITY_XML) String securityXml,
@FormParam("uniqueIdScheme") String uniqueIdScheme) {
uniqueIdScheme = StringUtils.trimToNull(uniqueIdScheme);
FlexiBean out = createRootData();
ExternalScheme scheme = ExternalScheme.of(idScheme);
out.put("requestScheme", scheme);
type = StringUtils.defaultString(StringUtils.trimToNull(type));
switch (type) {
case "xml":
securityXml = StringUtils.trimToNull(securityXml);
if (securityXml == null) {
return Response.status(Status.BAD_REQUEST).build();
}
ManageableSecurity security = addSecurity(securityXml, uniqueIdScheme);
out.put("addedSecurities", getAddedSecurityId(security));
break;
case StringUtils.EMPTY: // create security by ID if type is missing
case "id":
idScheme = StringUtils.trimToNull(idScheme);
idValue = StringUtils.trimToNull(idValue);
if (idScheme == null || idValue == null) {
return Response.status(Status.BAD_REQUEST).build();
}
Collection<ExternalIdBundle> requestBundles = buildSecurityRequest(scheme, idValue);
SecurityLoaderResult loaderResult = data().getSecurityLoader().loadSecurities(SecurityLoaderRequest.create(requestBundles));
out.put("addedSecurities", getLoadedSecuritiesId(loaderResult.getResultMap(), requestBundles, scheme));
break;
default:
throw new IllegalArgumentException("Can only add security by XML or ID");
}
return Response.ok(getFreemarker().build(JSON_DIR + "securities-added.ftl", out)).build();
}
private ManageableSecurity addSecurity(String securityXml, String uniqueIdScheme) {
Bean securityBean = JodaBeanSerialization.deserializer().xmlReader().read(securityXml);
SecurityMaster securityMaster = data().getSecurityMaster();
ManageableSecurity manageableSecurity = (ManageableSecurity) securityBean;
if (uniqueIdScheme != null) {
manageableSecurity.setUniqueId(UniqueId.of(uniqueIdScheme, uniqueIdScheme));
}
SecurityDocument addedSecDoc = securityMaster.add(new SecurityDocument(manageableSecurity));
return addedSecDoc.getSecurity();
}
private Map<String, String> getAddedSecurityId(ManageableSecurity security) {
Map<String, String> addedSecurities = new HashMap<>();
ExternalIdBundle externalIdBundle = security.getExternalIdBundle();
UniqueId uniqueId = security.getUniqueId();
String objectIdentifier = uniqueId.getObjectId().toString();
String externalIdValue = StringUtils.EMPTY;
if (!externalIdBundle.isEmpty()) {
ExternalId externalId = externalIdBundle.iterator().next();
externalIdValue = externalId.getValue();
}
addedSecurities.put(externalIdValue, objectIdentifier);
return addedSecurities;
}
private Map<String, String> getLoadedSecuritiesId(Map<ExternalIdBundle, UniqueId> loadedSecurities, Collection<ExternalIdBundle> requestBundles, ExternalScheme scheme) {
Map<String, String> result = new HashMap<String, String>();
for (ExternalIdBundle identifierBundle : requestBundles) {
UniqueId uniqueIdentifier = loadedSecurities.get(identifierBundle);
String objectIdentifier = uniqueIdentifier != null ? uniqueIdentifier.getObjectId().toString() : null;
result.put(identifierBundle.getValue(scheme), objectIdentifier);
}
return result;
}
private ExternalIdBundle buildRequestAsExternalIdBundle(ExternalScheme scheme, Collection<ExternalIdBundle> bundles) {
List<ExternalId> identifiers = new ArrayList<ExternalId>();
for (ExternalIdBundle bundle : bundles) {
identifiers.add(bundle.getExternalId(scheme));
}
return ExternalIdBundle.of(identifiers);
}
private Collection<ExternalIdBundle> buildSecurityRequest(final ExternalScheme identificationScheme, final String idValue) {
if (idValue == null) {
return Collections.emptyList();
}
final String[] identifiers = StringUtils.split(idValue, "\n");
final List<ExternalIdBundle> result = new ArrayList<ExternalIdBundle>(identifiers.length);
for (String identifier : identifiers) {
identifier = StringUtils.trimToNull(identifier);
if (identifier != null) {
result.add(ExternalIdBundle.of(ExternalId.of(identificationScheme, identifier)));
}
}
return result;
}
//-------------------------------------------------------------------------
@GET
@Path("metaData")
@Produces(MediaType.APPLICATION_JSON)
public String getMetaDataJSON(@QueryParam("uniqueIdScheme") String uniqueIdScheme) {
uniqueIdScheme = StringUtils.trimToNull(uniqueIdScheme);
FlexiBean out = super.createRootData();
out.put("schemaVersion", getSecurityMasterSchemaVersion(uniqueIdScheme));
out.put("securityTypes", data().getSecurityTypes().values());
out.put("description2type", data().getSecurityTypes());
return getFreemarker().build(JSON_DIR + "metadata.ftl", out);
}
private String getSecurityMasterSchemaVersion(String uniqueIdScheme) {
final SecurityMetaDataRequest request = new SecurityMetaDataRequest();
request.setUniqueIdScheme(uniqueIdScheme);
request.setSchemaVersion(true);
request.setSecurityTypes(false);
SecurityMetaDataResult metaData = data().getSecurityMaster().metaData(request);
return metaData.getSchemaVersion();
}
//-------------------------------------------------------------------------
@Path("{securityId}")
public WebSecurityResource findSecurity(@Subscribe @PathParam("securityId") String idStr) {
data().setUriSecurityId(idStr);
UniqueId oid = UniqueId.parse(idStr);
try {
SecurityDocument doc = data().getSecurityMaster().get(oid);
data().setSecurity(doc);
} catch (DataNotFoundException ex) {
SecurityHistoryRequest historyRequest = new SecurityHistoryRequest(oid);
historyRequest.setPagingRequest(PagingRequest.ONE);
SecurityHistoryResult historyResult = data().getSecurityMaster().history(historyRequest);
if (historyResult.getDocuments().size() == 0) {
throw ex;
}
data().setSecurity(historyResult.getFirstDocument());
}
return new WebSecurityResource(this);
}
//-------------------------------------------------------------------------
/**
* Creates the output root data.
* @return the output root data, not null
*/
@Override
protected FlexiBean createRootData() {
FlexiBean out = super.createRootData();
SecuritySearchRequest searchRequest = new SecuritySearchRequest();
out.put("searchRequest", searchRequest);
final SecurityMetaDataRequest request = new SecurityMetaDataRequest();
request.setSchemaVersion(true);
request.setSecurityTypes(false);
if (data().getSecurityMaster() instanceof DelegatingSecurityMaster) {
Map<String, String> schemaVersionByScheme = new HashMap<>();
DelegatingSecurityMaster delegatingSecMaster = (DelegatingSecurityMaster) data().getSecurityMaster();
Map<String, SecurityMaster> delegates = delegatingSecMaster.getDelegates();
for (Entry<String, SecurityMaster> entry : delegates.entrySet()) {
SecurityMaster securityMaster = entry.getValue();
SecurityMetaDataResult metaData = securityMaster.metaData(request);
schemaVersionByScheme.put(entry.getKey(), metaData.getSchemaVersion());
}
out.put("schemaVersionByScheme", schemaVersionByScheme);
out.put("uniqueIdSchemes", schemaVersionByScheme.keySet());
} else {
SecurityMetaDataResult metaData = data().getSecurityMaster().metaData(request);
out.put("schemaVersion", metaData.getSchemaVersion());
}
out.put("description2type", data().getSecurityTypes());
return out;
}
//-------------------------------------------------------------------------
/**
* Builds a URI for securities.
* @param data the data, not null
* @return the URI, not null
*/
public static URI uri(WebSecuritiesData data) {
return uri(data, null);
}
/**
* Builds a URI for securities.
* @param data the data, not null
* @param identifiers the identifiers to search for, may be null
* @return the URI, not null
*/
public static URI uri(WebSecuritiesData data, ExternalIdBundle identifiers) {
UriBuilder builder = data.getUriInfo().getBaseUriBuilder().path(WebSecuritiesResource.class);
if (identifiers != null) {
Iterator<ExternalId> it = identifiers.iterator();
for (int i = 0; it.hasNext(); i++) {
ExternalId id = it.next();
builder.queryParam("idscheme." + i, id.getScheme().getName());
builder.queryParam("idvalue." + i, id.getValue());
}
}
return builder.build();
}
}