From fcbdca66e4612e935923df620badb5334fce355a Mon Sep 17 00:00:00 2001 From: Bernd Hufmann Date: Tue, 22 Oct 2024 15:24:34 -0400 Subject: [PATCH] server: Implement support for customizable data providers Update skeleton endpoints in DataProviderService for that. Use ITmfDataProviderConfigurator interface for that. Signed-off-by: Bernd Hufmann --- .../core/services/DataProviderService.java | 203 +++++++++++++++--- .../rest/core/services/EndpointConstants.java | 13 ++ 2 files changed, 189 insertions(+), 27 deletions(-) diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java index f82075e09..e15030090 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java @@ -48,12 +48,15 @@ import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.MISSING_OUTPUTID; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.MISSING_PARAMETERS; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_PROVIDER; -import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_CONFIGURATION; +import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_CONFIGURATION_TYPE; +import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_DERIVED_PROVIDER; +import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_PROVIDER; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_TRACE; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.OCG; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.ONE_OF; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.OUTPUT_ID; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.PROVIDER_NOT_FOUND; +import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.PROVIDER_CONFIG_NOT_FOUND; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.STY; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.TABLE_TIMES; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.TGR; @@ -139,10 +142,16 @@ import org.eclipse.tracecompass.tmf.analysis.xml.core.module.TmfXmlUtils; import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModuleHelper; import org.eclipse.tracecompass.tmf.core.analysis.TmfAnalysisManager; +import org.eclipse.tracecompass.tmf.core.config.ITmfConfiguration; +import org.eclipse.tracecompass.tmf.core.config.ITmfConfigurationSourceType; +import org.eclipse.tracecompass.tmf.core.config.ITmfDataProviderConfigurator; +import org.eclipse.tracecompass.tmf.core.config.TmfConfiguration; import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager; import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils; import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor; import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType; +import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory; +import org.eclipse.tracecompass.tmf.core.exceptions.TmfConfigurationException; import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage; import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor; import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider; @@ -252,16 +261,11 @@ public Response getProvider( if (experiment == null) { return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build(); } - List list = DataProviderManager.getInstance().getAvailableProviders(experiment); - list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.TIME_GRAPH))); - list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.XY))); - Optional provider = list.stream().filter(p -> p.getId().equals(outputId)).findFirst(); - - if (provider.isPresent()) { - return Response.ok(provider.get()).build(); + IDataProviderDescriptor provider = getDescriptor(experiment, outputId); + if (provider != null) { + return Response.ok(provider).build(); } - return Response.status(Status.NOT_FOUND).build(); } @@ -1152,23 +1156,33 @@ public Response getStyles( @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get the list of configuration types defined on the server for a given output and experiment", responses = { @ApiResponse(responseCode = "200", description = "Returns a list of configuration types that this output supports.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.ConfigurationSourceType.class)))), - @ApiResponse(responseCode = "404", description = PROVIDER_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "404", description = PROVIDER_CONFIG_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), }) public Response getConfigurationTypes( @Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID, @Parameter(description = OUTPUT_ID) @PathParam("outputId") String outputId ) { - return Response.status(Status.NOT_IMPLEMENTED).entity("Get all configuration types for a given output").build(); //$NON-NLS-1$ - } - private static Response validateParameters(String outputId, QueryParameters queryParameters) { + TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID); + if (experiment == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build(); + } + if (outputId == null) { return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID).build(); } - if (queryParameters == null) { - return Response.status(Status.BAD_REQUEST).entity(MISSING_PARAMETERS).build(); + + IDataProviderDescriptor sourceDescriptor = getDescriptor(experiment, outputId); + IDataProviderFactory factory = manager.getFactory(outputId); + if (sourceDescriptor == null || factory == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); } - return null; + + ITmfDataProviderConfigurator configurator = factory.getAdapter(ITmfDataProviderConfigurator.class); + if (configurator != null) { + return Response.ok(configurator.getConfigurationSourceTypes()).build(); + } + return Response.ok(Collections.emptyList()).build(); } /** @@ -1188,14 +1202,40 @@ private static Response validateParameters(String outputId, QueryParameters quer @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get a single configuration source type defined on the server for a given data provider and experiment.", responses = { @ApiResponse(responseCode = "200", description = "Returns a single configuration source type", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.ConfigurationSourceType.class))), - @ApiResponse(responseCode = "404", description = PROVIDER_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "404", description = NO_SUCH_CONFIGURATION, content = @Content(schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "404", description = PROVIDER_CONFIG_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), }) public Response getConfigurationType( @Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID, @Parameter(description = OUTPUT_ID) @PathParam("outputId") String outputId, @Parameter(description = CFG_TYPE_ID) @PathParam("typeId") String typeId) { - return Response.status(Status.NOT_IMPLEMENTED).entity("Get a configuration type for a given output and typeId").build(); //$NON-NLS-1$ + + TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID); + if (experiment == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build(); + } + + if (outputId == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID).build(); + } + + IDataProviderDescriptor sourceDescriptor = getDescriptor(experiment, outputId); + IDataProviderFactory factory = manager.getFactory(outputId); + if (sourceDescriptor == null || factory == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + + ITmfDataProviderConfigurator configurator = factory.getAdapter(ITmfDataProviderConfigurator.class); + if (configurator == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + + Optional optional = configurator.getConfigurationSourceTypes().stream().filter(type -> type.getId().equals(typeId)).findAny(); + + if (!optional.isPresent()) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_CONFIGURATION_TYPE).build(); //$NON-NLS-1$ + } + return Response.ok(optional.get()).build(); } /** @@ -1210,6 +1250,7 @@ public Response getConfigurationType( * the query parameters used to create a data provider * @return a list of data provider descriptors */ + @SuppressWarnings("null") @POST @Path("/{outputId}") @Tag(name = OCG) @@ -1218,8 +1259,7 @@ public Response getConfigurationType( @Operation(summary = "Get the list of outputs for this configuration", responses = { @ApiResponse(responseCode = "200", description = "Returns a list of output provider descriptors", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DataProvider.class)))), @ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "404", description = PROVIDER_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "404", description = NO_SUCH_CONFIGURATION, content = @Content(schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "404", description = PROVIDER_CONFIG_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), }) public Response createDataProvider( @Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID, @@ -1227,7 +1267,45 @@ public Response createDataProvider( @RequestBody(description = CFG_CREATE_DESC + " " + CFG_KEYS_DESC, content = { @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.ConfigurationQueryParameters.class)) }, required = true) ConfigurationQueryParameters queryParameters) { - return Response.status(Status.NOT_IMPLEMENTED).entity("POST a configuration to a given output to create derived output").build(); //$NON-NLS-1$ + + try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#createDataProvider") //$NON-NLS-1$ + .setCategory(outputId).build()) { + TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID); + if (experiment == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build(); + } + + Response errorResponse = validateOutputConfigParameters(outputId, queryParameters); + if (errorResponse != null) { + return errorResponse; + } + + IDataProviderFactory factory = manager.getFactory(outputId); + if (factory == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + + ITmfDataProviderConfigurator configurator = factory.getAdapter(ITmfDataProviderConfigurator.class); + if (configurator == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + + ITmfConfiguration inputConfig = new TmfConfiguration.Builder() + .setName(queryParameters.getName()) + .setDescription(queryParameters.getDescription()) + .setSourceTypeId(queryParameters.getTypeId()) + .setParameters(queryParameters.getParameters()) + .build(); + String typeId = inputConfig.getSourceTypeId(); + Optional optional = configurator.getConfigurationSourceTypes().stream().filter(type -> type.getId().equals(typeId)).findAny(); + if (!optional.isPresent()) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_CONFIGURATION_TYPE).build(); + } + IDataProviderDescriptor returnDescr = configurator.createDataProviderDescriptors(experiment, inputConfig); + return Response.ok(returnDescr).build(); + } catch (TmfConfigurationException e) { + return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); + } } /** @@ -1245,17 +1323,88 @@ public Response createDataProvider( @Path("/{outputId}/{derivedOutputId}") @Tag(name = OCG) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Delete a configuration instance of a given configuration source type", responses = { + @Operation(summary = "Delete a configuration instance of a given configuration type", responses = { @ApiResponse(responseCode = "200", description = "The derived data provider (and it's configuration) was successfully deleted", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Configuration.class))), - @ApiResponse(responseCode = "404", description = PROVIDER_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "404", description = NO_SUCH_CONFIGURATION, content = @Content(schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "500", description = "Internal trace-server error while trying to delete output", content = @Content(schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "404", description = PROVIDER_CONFIG_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))), }) public Response deleteDerivedOutput( @Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID, @Parameter(description = OUTPUT_ID) @PathParam("outputId") String outputId, @Parameter(description = OUTPUT_ID) @PathParam("derivedOutputId") String derivedOutputId) { - return Response.status(Status.NOT_IMPLEMENTED).entity("DELETE derived output").build(); //$NON-NLS-1$ + + if (outputId == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID + " (parent)").build(); //$NON-NLS-1$ + } + + if (derivedOutputId == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID + " (derived)").build(); //$NON-NLS-1$ + } + + try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#removeDataProvider") //$NON-NLS-1$ + .setCategory(outputId).build()) { + TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID); + if (experiment == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build(); + } + + IDataProviderDescriptor parentDescriptor = getDescriptor(experiment, outputId); + if (parentDescriptor == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER + ": " + outputId).build(); //$NON-NLS-1$ + } + + IDataProviderDescriptor derivedDescriptor = getDescriptor(experiment, derivedOutputId); + if (derivedDescriptor == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_DERIVED_PROVIDER + ": " + derivedOutputId).build(); //$NON-NLS-1$ + } + + IDataProviderFactory factory = manager.getFactory(outputId); + if (factory == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + + ITmfDataProviderConfigurator configurator = factory.getAdapter(ITmfDataProviderConfigurator.class); + if (configurator == null) { + return Response.status(Status.NOT_FOUND).entity(NO_SUCH_PROVIDER).build(); + } + configurator.removeDataProviderDescriptor(experiment, derivedDescriptor); + return Response.ok().build(); + } catch (TmfConfigurationException e) { + return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); + } + } + + private static Response validateParameters(String outputId, QueryParameters queryParameters) { + if (outputId == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID).build(); + } + if (queryParameters == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_PARAMETERS).build(); + } + return null; + } + + private static Response validateOutputConfigParameters(String outputId, ConfigurationQueryParameters queryParameters) { + if (outputId == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_OUTPUTID).build(); + } + if (queryParameters == null) { + return Response.status(Status.BAD_REQUEST).entity(MISSING_PARAMETERS).build(); + } + + return null; + } + + private @Nullable IDataProviderDescriptor getDescriptor(@NonNull ITmfTrace experiment, @NonNull String outputId) { + List list = manager.getAvailableProviders(experiment); + list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.TIME_GRAPH))); + list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.XY))); + + Optional provider = list.stream().filter(p -> p.getId().equals(outputId)).findFirst(); + if (provider.isPresent()) { + return provider.get(); + } + return null; } } diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/EndpointConstants.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/EndpointConstants.java index 7bdbdefb6..b022409b8 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/EndpointConstants.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/EndpointConstants.java @@ -34,12 +34,24 @@ public final class EndpointConstants { /** Error message returned for a request with missing parameters */ public static final String MISSING_PARAMETERS = "Missing query parameters"; //$NON-NLS-1$ + /** Error message returned for a request with configuration type ID */ + public static final String MISSING_TYPE_ID = "Missing configuration type ID"; //$NON-NLS-1$ + /** Error message returned for a request with invalid parameters */ public static final String INVALID_PARAMETERS = "Invalid query parameters"; //$NON-NLS-1$ /** Error message returned for a request for a non-existing data provider */ public static final String NO_PROVIDER = "Analysis cannot run"; //$NON-NLS-1$ + /** Error message returned for a request for trace that doesn't exist */ + public static final String NO_SUCH_CONFIGURATION_TYPE = "No such configuration type"; //$NON-NLS-1$ + + /** Error message returned for a request for a non-existing derived data provider */ + public static final String NO_SUCH_DERIVED_PROVIDER = "Derived data provider doesn't exist"; //$NON-NLS-1$ + + /** Error message returned for a request for a non-existing data provider */ + public static final String NO_SUCH_PROVIDER = "Data provider doesn't exist"; //$NON-NLS-1$ + /** Error message returned for a request for trace that doesn't exist */ public static final String NO_SUCH_TRACE = "No such trace"; //$NON-NLS-1$ @@ -166,6 +178,7 @@ public final class EndpointConstants { static final String TRACE_CREATION_FAILED = "Trace resource creation failed"; //$NON-NLS-1$ static final String TREE_ENTRIES = "Unique entry point for output providers, to get the tree of visible entries"; //$NON-NLS-1$ static final String NO_SUCH_CONFIGURATION = "No such configuration source type or configuration instance"; //$NON-NLS-1$ + static final String PROVIDER_CONFIG_NOT_FOUND = "Experiment, output provider or configuration type not found"; //$NON-NLS-1$ private EndpointConstants() { // private constructor