/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.analytics.rest; import java.net.URI; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Instant; import org.threeten.bp.LocalDateTime; import org.threeten.bp.format.DateTimeFormatter; import com.google.common.collect.ImmutableMap; import com.opengamma.engine.marketdata.spec.MarketDataSpecification; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.view.client.ViewClient; import com.opengamma.engine.view.client.ViewClientState; import com.opengamma.id.UniqueId; import com.opengamma.id.VersionCorrection; import com.opengamma.livedata.UserPrincipal; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.OpenGammaClock; import com.opengamma.util.auth.AuthUtils; import com.opengamma.util.rest.RestUtils; import com.opengamma.util.tuple.Pair; import com.opengamma.web.analytics.AnalyticsView; import com.opengamma.web.analytics.AnalyticsViewManager; import com.opengamma.web.analytics.ErrorInfo; import com.opengamma.web.analytics.GridCell; import com.opengamma.web.analytics.GridStructure; import com.opengamma.web.analytics.MarketDataSpecificationJsonReader; import com.opengamma.web.analytics.ValueRequirementTargetForCell; import com.opengamma.web.analytics.ViewRequest; import com.opengamma.web.analytics.ViewportDefinition; import com.opengamma.web.analytics.ViewportResults; import com.opengamma.web.analytics.formatting.TypeFormatter; import com.opengamma.web.analytics.json.ValueRequirementFormParam; import com.opengamma.web.analytics.push.ClientConnection; import com.opengamma.web.analytics.push.ConnectionManager; /** * REST resource for the analytics grid. This resource class specifies the endpoints of every object in the * hierarchy of grids, dependency graphs and viewports in the analytics viewer. */ @Path("views") public class WebUiResource { private static final Logger s_logger = LoggerFactory.getLogger(WebUiResource.class); private static final DateTimeFormatter CSV_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss"); /** For generating IDs for the views. */ private static final AtomicLong s_nextViewId = new AtomicLong(0); /** For generating IDs for the viewports and dependency graphs. */ private static final AtomicInteger s_nextId = new AtomicInteger(0); /** For creating and retrieving views. */ private final AnalyticsViewManager _viewManager; /** For looking up a client's connection. */ private final ConnectionManager _connectionManager; public WebUiResource(AnalyticsViewManager viewManager, ConnectionManager connectionManager) { ArgumentChecker.notNull(viewManager, "viewManager"); ArgumentChecker.notNull(connectionManager, "connectionManager"); _viewManager = viewManager; _connectionManager = connectionManager; } @POST public Response createView(@Context UriInfo uriInfo, @Context HttpServletRequest httpRequest, @FormParam("requestId") String requestId, @FormParam("viewDefinitionId") String viewDefinitionId, @FormParam("aggregators") List<String> aggregators, @FormParam("marketDataProviders") String marketDataProviders, @FormParam("valuationTime") String valuationTime, @FormParam("portfolioVersionTime") String portfolioVersionTime, @FormParam("portfolioCorrectionTime") String portfolioCorrectionTime, @FormParam("clientId") String clientId, @FormParam("blotter") Boolean blotter) { ArgumentChecker.notEmpty(requestId, "requestId"); ArgumentChecker.notEmpty(viewDefinitionId, "viewDefinitionId"); ArgumentChecker.notNull(aggregators, "aggregators"); ArgumentChecker.notEmpty(marketDataProviders, "marketDataProviders"); ArgumentChecker.notEmpty(clientId, "clientId"); boolean blotterColumns = blotter == null ? false : blotter; List<MarketDataSpecification> marketDataSpecs = MarketDataSpecificationJsonReader.buildSpecifications(marketDataProviders); VersionCorrection versionCorrection = VersionCorrection.of(parseInstant(portfolioVersionTime), parseInstant(portfolioCorrectionTime)); ViewRequest viewRequest = _viewManager.createViewRequest(UniqueId.parse(viewDefinitionId), aggregators, marketDataSpecs, parseInstant(valuationTime), versionCorrection, blotterColumns); String viewId = Long.toString(s_nextViewId.getAndIncrement()); URI portfolioGridUri = uriInfo.getAbsolutePathBuilder() .path(viewId) .path("portfolio") .build(); URI primitivesGridUri = uriInfo.getAbsolutePathBuilder() .path(viewId) .path("primitives") .build(); String userName = (AuthUtils.isPermissive() ? null : AuthUtils.getUserName()); ClientConnection connection = _connectionManager.getConnectionByClientId(userName, clientId); URI uri = uriInfo.getAbsolutePathBuilder().path(viewId).build(); ImmutableMap<String, Object> callbackMap = ImmutableMap.<String, Object>of("id", requestId, "message", uri.getPath()); URI errorUri = uriInfo.getAbsolutePathBuilder() .path(viewId) .path("errors") .build(); // Get session id or create one String sessionId = "session-id:" + httpRequest.getSession().getId(); // Track user principal using session id rather than ip address UserPrincipal ogUserPrincipal = userName != null ? new UserPrincipal(userName, sessionId) : UserPrincipal.getTestUser(); _viewManager.createView(viewRequest, clientId, ogUserPrincipal, connection, viewId, callbackMap, portfolioGridUri.getPath(), primitivesGridUri.getPath(), errorUri.getPath()); return Response.status(Response.Status.CREATED).build(); } @Path("{viewId}") @DELETE public void deleteView(@PathParam("viewId") String viewId) { _viewManager.deleteView(viewId); } @Path("{viewId}/pauseOrResume") @PUT public Response pauseOrResumeView(@PathParam("viewId") String viewId, @FormParam("state") String state) { ViewClient viewClient = _viewManager.getViewCient(viewId); state = StringUtils.stripToNull(state); Response response = Response.status(Response.Status.BAD_REQUEST).build(); if (state != null) { ViewClientState currentState = viewClient.getState(); state = state.toUpperCase(); switch (state) { case "PAUSE": case "P": if (currentState != ViewClientState.TERMINATED) { viewClient.pause(); response = Response.ok().build(); } break; case "RESUME": case "R": if (currentState != ViewClientState.TERMINATED) { viewClient.resume(); response = Response.ok().build(); } break; default: s_logger.warn("client {} requesting for invalid view client state change to {}", viewId, state); response = Response.status(Response.Status.BAD_REQUEST).build(); break; } } return response; } @Path("{viewId}/{gridType}") @GET public GridStructure getGridStructure(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType) { return _viewManager.getView(viewId).getInitialGridStructure(gridType(gridType)); } @Path("{viewId}/{gridType}/viewports/{viewportId}/valuereq/{row}/{col}") @GET public ValueRequirementTargetForCell getValueRequirementForTargetForCell(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("row") int row, @PathParam("col") int col, @PathParam("viewportId") int viewportId) { GridStructure gridStructure = _viewManager.getView(viewId).getGridStructure(gridType(gridType), viewportId); Pair<String, ValueRequirement> pair = gridStructure.getValueRequirementForCell(row, col); return new ValueRequirementTargetForCell(pair.getFirst(), pair.getSecond()); } @Path("{viewId}/{gridType}/viewports") @POST public Response createViewport(@Context UriInfo uriInfo, @PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @FormParam("requestId") int requestId, @FormParam("version") int version, @FormParam("rows") List<Integer> rows, @FormParam("columns") List<Integer> columns, @FormParam("cells") List<GridCell> cells, @FormParam("format") TypeFormatter.Format format, @FormParam("enableLogging") Boolean enableLogging) { ViewportDefinition viewportDefinition = ViewportDefinition.create(version, rows, columns, cells, format, enableLogging); int viewportId = s_nextId.getAndIncrement(); String viewportIdStr = Integer.toString(viewportId); UriBuilder viewportUriBuilder = uriInfo.getAbsolutePathBuilder().path(viewportIdStr); String callbackId = viewportUriBuilder.build().getPath(); String structureCallbackId = viewportUriBuilder.path("structure").build().getPath(); _viewManager.getView(viewId).createViewport(requestId, gridType(gridType), viewportId, callbackId, structureCallbackId, viewportDefinition); return Response.status(Response.Status.CREATED).build(); } @Path("{viewId}/{gridType}/viewports/{viewportId}") @PUT public void updateViewport(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("viewportId") int viewportId, @FormParam("version") int version, @FormParam("rows") List<Integer> rows, @FormParam("columns") List<Integer> columns, @FormParam("cells") List<GridCell> cells, @FormParam("format") TypeFormatter.Format format, @FormParam("enableLogging") Boolean enableLogging) { ViewportDefinition viewportDef = ViewportDefinition.create(version, rows, columns, cells, format, enableLogging); _viewManager.getView(viewId).updateViewport(gridType(gridType), viewportId, viewportDef); } @Path("{viewId}/{gridType}/viewports/{viewportId}/structure") @GET public GridStructure getViewportGridStructure(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("viewportId") int viewportId) { return _viewManager.getView(viewId).getGridStructure(gridType(gridType), viewportId); } @Path("{viewId}/{gridType}/viewports/{viewportId}") @GET public ViewportResults getViewportData(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("viewportId") int viewportId) { return _viewManager.getView(viewId).getData(gridType(gridType), viewportId); } @Path("{viewId}/{gridType}/viewports/{viewportId}") @DELETE public void deleteViewport(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("viewportId") int viewportId) { _viewManager.getView(viewId).deleteViewport(gridType(gridType), viewportId); } @Path("{viewId}/{gridType}/depgraphs") @POST public Response openDependencyGraph(@Context UriInfo uriInfo, @PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @FormParam("requestId") int requestId, @FormParam("row") Integer row, @FormParam("col") Integer col, @FormParam("colset") String calcConfigName, @FormParam("req") ValueRequirementFormParam valueRequirementParam) { int graphId = s_nextId.getAndIncrement(); String graphIdStr = Integer.toString(graphId); URI graphUri = uriInfo.getAbsolutePathBuilder().path(graphIdStr).build(); String callbackId = graphUri.getPath(); if (row != null && col != null) { _viewManager.getView(viewId).openDependencyGraph(requestId, gridType(gridType), graphId, callbackId, row, col); } else if (calcConfigName != null && valueRequirementParam != null) { ValueRequirement valueRequirement = valueRequirementParam.getValueRequirement(); _viewManager.getView(viewId).openDependencyGraph(requestId, gridType(gridType), graphId, callbackId, calcConfigName, valueRequirement); } return Response.status(Response.Status.CREATED).build(); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}") @GET public GridStructure getDependencyGraphGridStructure(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId) { return _viewManager.getView(viewId).getInitialGridStructure(gridType(gridType), depgraphId); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}") @DELETE public void deleteDependencyGraph(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId) { _viewManager.getView(viewId).closeDependencyGraph(gridType(gridType), depgraphId); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}/viewports") @POST public Response createDependencyGraphViewport(@Context UriInfo uriInfo, @PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId, @FormParam("requestId") int requestId, @FormParam("version") int version, @FormParam("rows") List<Integer> rows, @FormParam("columns") List<Integer> columns, @FormParam("cells") List<GridCell> cells, @FormParam("format") TypeFormatter.Format format, @FormParam("enableLogging") Boolean enableLogging) { ViewportDefinition viewportDefinition = ViewportDefinition.create(version, rows, columns, cells, format, enableLogging); int viewportId = s_nextId.getAndIncrement(); String viewportIdStr = Integer.toString(viewportId); UriBuilder viewportUriBuilder = uriInfo.getAbsolutePathBuilder().path(viewportIdStr); String callbackId = viewportUriBuilder.build().getPath(); String structureCallbackId = viewportUriBuilder.path("structure").build().getPath(); _viewManager.getView(viewId).createViewport(requestId, gridType(gridType), depgraphId, viewportId, callbackId, structureCallbackId, viewportDefinition); return Response.status(Response.Status.CREATED).build(); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}/viewports/{viewportId}") @PUT public void updateDependencyGraphViewport(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId, @PathParam("viewportId") int viewportId, @FormParam("version") int version, @FormParam("rows") List<Integer> rows, @FormParam("columns") List<Integer> columns, @FormParam("cells") List<GridCell> cells, @FormParam("format") TypeFormatter.Format format, @FormParam("enableLogging") Boolean enableLogging) { ViewportDefinition viewportDef = ViewportDefinition.create(version, rows, columns, cells, format, enableLogging); _viewManager.getView(viewId).updateViewport(gridType(gridType), depgraphId, viewportId, viewportDef); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}/viewports/{viewportId}/structure") @GET public GridStructure getDependencyGraphViewportGridStructure(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId, @PathParam("viewportId") int viewportId) { GridStructure g = _viewManager.getView(viewId).getGridStructure(gridType(gridType), depgraphId, viewportId); return g; } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}/viewports/{viewportId}") @GET public ViewportResults getDependencyGraphViewportData(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId, @PathParam("viewportId") int viewportId) { return _viewManager.getView(viewId).getData(gridType(gridType), depgraphId, viewportId); } @Path("{viewId}/{gridType}/depgraphs/{depgraphId}/viewports/{viewportId}") @DELETE public void deleteDependencyGraphViewport(@PathParam("viewId") String viewId, @PathParam("gridType") String gridType, @PathParam("depgraphId") int depgraphId, @PathParam("viewportId") int viewportId) { _viewManager.getView(viewId).deleteViewport(gridType(gridType), depgraphId, viewportId); } @Path("{viewId}/errors") @GET public List<ErrorInfo> getErrors(@PathParam("viewId") String viewId) { return _viewManager.getView(viewId).getErrors(); } @Path("{viewId}/errors/{errorId}") @DELETE public void deleteError(@PathParam("viewId") String viewId, @PathParam("errorId") long errorId) { _viewManager.getView(viewId).deleteError(errorId); } /** * Produces view port results as CSV * * @param response the injected servlet response, not null. * @param viewId ID of the view * @param gridTypeStr the grid type, 'portfolio' or 'primitives' * @return The view port result as csv */ @GET @Path("{viewId}/{gridType}/data") @Produces(RestUtils.TEXT_CSV) public ViewportResults getViewportResultAsCsv(@PathParam("viewId") String viewId, @PathParam("gridType") String gridTypeStr, @Context HttpServletResponse response) { AnalyticsView view = _viewManager.getView(viewId); AnalyticsView.GridType gridType = gridType(gridTypeStr); ViewportResults result = view.getAllGridData(gridType, TypeFormatter.Format.CELL); Instant valuationTime; if (result.getValuationTime() != null) { valuationTime = result.getValuationTime(); } else { valuationTime = OpenGammaClock.getInstance().instant(); } LocalDateTime time = LocalDateTime.ofInstant(valuationTime, OpenGammaClock.getZone()); String filename = String.format("%s-%s-%s.csv", view.getViewDefinitionId(), gridType.name().toLowerCase(), time.toString(CSV_TIME_FORMAT)); response.addHeader("content-disposition", "attachment; filename=\"" + filename + "\""); return view.getAllGridData(gridType, TypeFormatter.Format.CELL); } /** * @param instantString An ISO-8601 string representing an instant or null * @return The parsed string or null if the input is null */ private static Instant parseInstant(String instantString) { if (instantString == null) { return null; } else { return Instant.parse(instantString); } } private static AnalyticsView.GridType gridType(String gridType) { return AnalyticsView.GridType.valueOf(gridType.toUpperCase()); } }