/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.marketdatasnapshot; import static org.threeten.bp.temporal.ChronoUnit.SECONDS; import java.net.URI; 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.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.StringUtils; import org.joda.beans.impl.flexi.FlexiBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Instant; import org.threeten.bp.LocalDate; import org.threeten.bp.LocalTime; import org.threeten.bp.ZonedDateTime; import org.threeten.bp.format.DateTimeFormatter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.opengamma.DataNotFoundException; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.config.ConfigSource; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource; import com.opengamma.core.marketdatasnapshot.impl.ManageableMarketDataSnapshot; import com.opengamma.engine.ComputationTargetResolver; import com.opengamma.engine.marketdata.NamedMarketDataSpecificationRepository; import com.opengamma.engine.marketdata.live.LiveMarketDataProviderFactory; import com.opengamma.engine.marketdata.snapshot.MarketDataSnapshotter.Mode; import com.opengamma.engine.marketdata.spec.FixedHistoricalMarketDataSpecification; import com.opengamma.engine.marketdata.spec.LatestHistoricalMarketDataSpecification; import com.opengamma.engine.marketdata.spec.LiveMarketDataSpecification; import com.opengamma.engine.marketdata.spec.MarketDataSpecification; import com.opengamma.engine.marketdata.spec.UserMarketDataSpecification; import com.opengamma.engine.view.ViewDefinition; import com.opengamma.engine.view.ViewProcessor; import com.opengamma.financial.analytics.volatility.cube.VolatilityCubeDefinitionSource; import com.opengamma.financial.tool.marketdata.MarketDataSnapshotSaver; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueId; import com.opengamma.master.config.ConfigDocument; import com.opengamma.master.config.ConfigMaster; import com.opengamma.master.config.ConfigSearchRequest; import com.opengamma.master.config.impl.ConfigSearchIterator; import com.opengamma.master.historicaltimeseries.impl.HistoricalTimeSeriesRating; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotDocument; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotHistoryRequest; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotHistoryResult; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotMaster; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotSearchRequest; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotSearchResult; import com.opengamma.master.marketdatasnapshot.MarketDataSnapshotSearchSortOrder; import com.opengamma.master.marketdatasnapshot.impl.MarketDataSnapshotSearchIterator; 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 market data snapshot documents. * <p> * The market data snapshot documents resource represents the whole of a position master. * */ @SuppressWarnings("deprecation") @Path("/datasnapshots") public class WebMarketDataSnapshotsResource extends AbstractWebMarketDataSnapshotResource { private static final String CUSTOM_DATE_SUFFIX = "_CustomDate"; private static final Logger s_logger = LoggerFactory.getLogger(WebMarketDataSnapshotsResource.class); /** Time format: HH:mm:ss */ private static final DateTimeFormatter VALUATION_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final DateTimeFormatter HISORICAL_SNAPSHOT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); /** * Creates the resource. * @param marketSnapshotMaster the market data snapshot master, not null * @param configMaster the config master, not null * @param liveMarketDataProviderFactory the live market data provider factory, Either this or marketDataSpecificationRepository must be set * @param marketDataSpecificationRepository the market data specification repository * @param configSource the config source, not null * @param targetResolver the computation target resolver, not null * @param viewProcessor the view processor, not null * @param htsSource the historical timeseries source, not null * @param volatilityCubeDefinitionSource the volatility cube definition source, not null */ public WebMarketDataSnapshotsResource(final MarketDataSnapshotMaster marketSnapshotMaster, final ConfigMaster configMaster, final LiveMarketDataProviderFactory liveMarketDataProviderFactory, final NamedMarketDataSpecificationRepository marketDataSpecificationRepository, final ConfigSource configSource, final ComputationTargetResolver targetResolver, final ViewProcessor viewProcessor, final HistoricalTimeSeriesSource htsSource, final VolatilityCubeDefinitionSource volatilityCubeDefinitionSource) { super(marketSnapshotMaster, configMaster, liveMarketDataProviderFactory, marketDataSpecificationRepository, configSource, targetResolver, viewProcessor, htsSource, volatilityCubeDefinitionSource); } //------------------------------------------------------------------------- @GET @Produces(MediaType.TEXT_HTML) @SubscribeMaster(MasterType.MARKET_DATA_SNAPSHOT) public String getHTML( @QueryParam("pgIdx") Integer pgIdx, @QueryParam("pgNum") Integer pgNum, @QueryParam("pgSze") Integer pgSze, @QueryParam("sort") String sort, @QueryParam("name") String name, @QueryParam("snapshotId") List<String> snapshotIdStrs, @Context UriInfo uriInfo) { PagingRequest pr = buildPagingRequest(pgIdx, pgNum, pgSze); MarketDataSnapshotSearchSortOrder so = buildSortOrder(sort, MarketDataSnapshotSearchSortOrder.NAME_ASC); FlexiBean out = search(pr, so, name, snapshotIdStrs, uriInfo); return getFreemarker().build(HTML_DIR + "snapshots.ftl", out); } private FlexiBean search(PagingRequest request, MarketDataSnapshotSearchSortOrder so, String name, List<String> snapshotIdStrs, UriInfo uriInfo) { FlexiBean out = createRootData(); MarketDataSnapshotSearchRequest searchRequest = new MarketDataSnapshotSearchRequest(); searchRequest.setPagingRequest(request); searchRequest.setSortOrder(so); searchRequest.setName(StringUtils.trimToNull(name)); searchRequest.setIncludeData(false); out.put("searchRequest", searchRequest); for (String snapshotIdStr : snapshotIdStrs) { searchRequest.addMarketDataSnapshotId(ObjectId.parse(snapshotIdStr)); } if (data().getUriInfo().getQueryParameters().size() > 0) { MarketDataSnapshotSearchResult searchResult = data().getMarketDataSnapshotMaster().search(searchRequest); out.put("searchResult", searchResult); out.put("paging", new WebPaging(searchResult.getPaging(), uriInfo)); } return out; } //------------------------------------------------------------------------- @Path("{snapshotId}") public WebMarketDataSnapshotResource findSnapshot(@Subscribe @PathParam("snapshotId") String idStr) { data().setUriSnapshotId(idStr); UniqueId oid = UniqueId.parse(idStr); try { MarketDataSnapshotDocument doc = data().getMarketDataSnapshotMaster().get(oid); data().setSnapshot(doc); } catch (DataNotFoundException ex) { MarketDataSnapshotHistoryRequest historyRequest = new MarketDataSnapshotHistoryRequest(oid); historyRequest.setPagingRequest(PagingRequest.ONE); MarketDataSnapshotHistoryResult historyResult = data().getMarketDataSnapshotMaster().history(historyRequest); if (historyResult.getDocuments().size() == 0) { throw ex; } data().setSnapshot(historyResult.getFirstDocument()); } return new WebMarketDataSnapshotResource(this); } //------------------------------------------------------------------------- /** * Creates the output root data. * @return the output root data, not null */ @Override protected FlexiBean createRootData() { FlexiBean out = super.createRootData(); MarketDataSnapshotSearchRequest searchRequest = new MarketDataSnapshotSearchRequest(); out.put("searchRequest", searchRequest); out.put("views", getViewNames()); out.put("liveDataSources", getLiveDataSources()); out.put("timeseriesresolverkeys", getTimeSeriesResolverKeys()); out.put("snapshots", getSnapshots()); return out; } private List<ManageableMarketDataSnapshot> getSnapshots() { MarketDataSnapshotSearchRequest snapshotSearchRequest = new MarketDataSnapshotSearchRequest(); snapshotSearchRequest.setIncludeData(false); List<ManageableMarketDataSnapshot> snapshots = Lists.newArrayList(); for (MarketDataSnapshotDocument doc : MarketDataSnapshotSearchIterator.iterable(data().getMarketDataSnapshotMaster(), snapshotSearchRequest)) { snapshots.add(doc.getSnapshot()); } return snapshots; } private List<String> getTimeSeriesResolverKeys() { ConfigSearchRequest<HistoricalTimeSeriesRating> request = new ConfigSearchRequest<HistoricalTimeSeriesRating>(HistoricalTimeSeriesRating.class); final List<String> keyNames = Lists.newArrayList(); for (ConfigDocument doc : ConfigSearchIterator.iterable(data().getConfigMaster(), request)) { keyNames.add(doc.getName()); } return keyNames; } private List<String> getLiveDataSources() { List<String> liveDataSources; if (data().getLiveMarketDataProviderFactory() != null) { liveDataSources = data().getLiveMarketDataProviderFactory().getProviderNames(); } else if (data().getMarketDataSpecificationRepository() != null) { liveDataSources = data().getMarketDataSpecificationRepository().getNames(); } else { liveDataSources = ImmutableList.of(); } return liveDataSources; } private List<String> getViewNames() { ConfigMaster configMaster = data().getConfigMaster(); ConfigSearchRequest<ViewDefinition> request = new ConfigSearchRequest<ViewDefinition>(); request.setType(ViewDefinition.class); List<String> viewNames = Lists.newArrayList(); for (ConfigDocument doc : ConfigSearchIterator.iterable(configMaster, request)) { ViewDefinition viewDefintion = (ViewDefinition) doc.getValue().getValue(); if (viewDefintion.getName() != null) { viewNames.add(viewDefintion.getName()); } } Collections.sort(viewNames); return viewNames; } //------------------------------------------------------------------------- @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.TEXT_HTML) public Response postHTML( @FormParam("name") String name, @FormParam("view") String view, @FormParam("valuationTime") String valuationTime, @FormParam("tsResolverKeys") List<String> tsResolverKeys, @FormParam("userSnapshotIds") List<String> userSnapshotIds, @FormParam("liveDataSources") List<String> liveDataSources, MultivaluedMap<String, String> formParameters) { name = StringUtils.trimToNull(name); view = StringUtils.trimToNull(view); valuationTime = StringUtils.trimToNull(valuationTime); Instant valuationInstant = null; if (valuationTime != null) { try { final LocalTime time = LocalTime.parse(valuationTime, VALUATION_TIME_FORMATTER); valuationInstant = ZonedDateTime.now().with(time.truncatedTo(SECONDS)).toInstant(); } catch (Exception ex) { s_logger.warn("Invalid valuation time {}", valuationTime); } } else { valuationInstant = Instant.now(); } final List<MarketDataSpecification> marketDataSpecs = getMarketDataSpecs(tsResolverKeys, userSnapshotIds, liveDataSources, formParameters); if (view == null || marketDataSpecs.isEmpty() || valuationInstant == null) { FlexiBean out = createRootData(); if (valuationInstant == null) { out.put("err_valutionTimeInvalid", true); } if (view == null) { out.put("err_viewMissing", true); } else { out.put("selectedView", view); } if (marketDataSpecs.isEmpty()) { out.put("err_marketDataSpecsMissing", true); } else { out.put("selectedTSResolverMap", getTsResolverKeyInputs(tsResolverKeys, formParameters)); out.put("selectedUserSnapshotIds", userSnapshotIds); out.put("selectedLiveDataSources", liveDataSources); } //include other inputs MarketDataSnapshotSearchRequest searchRequest = new MarketDataSnapshotSearchRequest(); searchRequest.setName(name); out.put("searchRequest", searchRequest); out.put("valuationTime", valuationTime); String html = getFreemarker().build(HTML_DIR + "snapshot-add.ftl", out); return Response.ok(html).build(); } MarketDataSnapshotDocument snapshot = null; try { snapshot = createSnapshot(name, view, valuationInstant, marketDataSpecs); } catch (Exception ex) { s_logger.error("Unable to create market data snapshot"); throw new OpenGammaRuntimeException("Unable to create market data snapshot"); } URI uri = data().getUriInfo().getAbsolutePathBuilder().path(snapshot.getUniqueId().toLatest().toString()).build(); return Response.seeOther(uri).build(); } private Map<String, String> getTsResolverKeyInputs(List<String> tsResolverKeys, MultivaluedMap<String, String> formParameters) { Map<String, String> result = Maps.newHashMap(); for (String tsResolverKey : tsResolverKeys) { List<String> tsDates = formParameters.get(tsResolverKey + CUSTOM_DATE_SUFFIX); if (!tsDates.isEmpty()) { result.put(tsResolverKey, Iterables.getFirst(tsDates, null)); } } return result; } private List<MarketDataSpecification> getMarketDataSpecs(List<String> tsResolverKeys, List<String> userSnapshotIds, List<String> liveDataSources, MultivaluedMap<String, String> formParameters) { List<MarketDataSpecification> marketDataSpecs = Lists.newArrayList(); for (String liveDataSource : liveDataSources) { liveDataSource = StringUtils.trimToNull(liveDataSource); if (liveDataSource != null) { marketDataSpecs.add(LiveMarketDataSpecification.of(liveDataSource)); } } for (String tsResolverKey : tsResolverKeys) { List<String> tsDates = formParameters.get(tsResolverKey + CUSTOM_DATE_SUFFIX); if (tsDates.isEmpty()) { marketDataSpecs.add(new LatestHistoricalMarketDataSpecification(tsResolverKey)); } else { String tsDate = StringUtils.trimToNull(Iterables.getFirst(tsDates, null)); if (tsDate != null) { LocalDate snapshotDate = LocalDate.parse(tsDate, HISORICAL_SNAPSHOT_DATE_FORMATTER); marketDataSpecs.add(new FixedHistoricalMarketDataSpecification(tsResolverKey, snapshotDate)); } } } for (String userSnapshotId : userSnapshotIds) { userSnapshotId = StringUtils.trimToNull(userSnapshotId); if (userSnapshotId != null) { try { marketDataSpecs.add(UserMarketDataSpecification.of(UniqueId.parse(userSnapshotId))); } catch (IllegalArgumentException ex) { s_logger.warn("Illegal format in snapshot {}, ignorning... ", userSnapshotId); } } } return marketDataSpecs; } private MarketDataSnapshotDocument createSnapshot(String name, final String viewDefinitionName, final Instant valuationInstant, final List<MarketDataSpecification> marketDataSpecs) throws InterruptedException { MarketDataSnapshotSaver saver = MarketDataSnapshotSaver.of(data().getComputationTargetResolver(), data().getHistoricalTimeSeriesSource(), data().getViewProcessor(), data().getConfigMaster(), data().getMarketDataSnapshotMaster(), data().getVolatilityCubeDefinitionSource(), Mode.STRUCTURED, null); return saver.createSnapshot(name, viewDefinitionName, valuationInstant, marketDataSpecs); } //------------------------------------------------------------------------- /** * Builds a URI for snapshots. * @param data the data, not null * @return the URI, not null */ public static URI uri(WebMarketDataSnapshotData data) { UriBuilder builder = data.getUriInfo().getBaseUriBuilder().path(WebMarketDataSnapshotsResource.class); return builder.build(); } }