From 3fb8c2bf796fc74cb97c43c7fa34200807c59b95 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Mon, 21 Oct 2024 14:22:26 +0200 Subject: [PATCH] feat: introduces dymanic feature binding in WebService (#4559) * feat: introduces dymanic feature binding in WebService * feat: applies versioned JSON-LD scope to JsonLdRemoteMessageSerializerImpl * chore: add tests for dynamic feature in JerserRestServiceTest --- .../dsp-catalog-http-api/build.gradle.kts | 3 +- .../http/api/DspCatalogApiExtension.java | 12 ++++- .../DspApiConfigurationExtension.java | 23 ++++---- .../DspApiConfigurationExtensionTest.java | 24 +++++---- .../dsp/http/DspHttpCoreExtension.java | 2 +- .../JsonLdRemoteMessageSerializerImpl.java | 13 +++-- ...JsonLdRemoteMessageSerializerImplTest.java | 7 ++- .../dsp-negotiation-http-api/build.gradle.kts | 1 + .../http/api/DspNegotiationApiExtension.java | 16 ++++++ .../protocol/dsp/spi/type/DspConstants.java | 2 + .../build.gradle.kts | 1 + .../api/DspTransferProcessApiExtension.java | 14 +++++ .../dsp-version-http-api/build.gradle.kts | 1 + .../http/api/DspVersionApiExtension.java | 14 +++++ .../edc/web/jersey/JerseyRestService.java | 13 +++++ .../feature/DynamicResourceFeature.java | 43 +++++++++++++++ .../edc/web/jersey/JerseyRestServiceTest.java | 54 ++++++++++++++++++- .../feature/DynamicResourceFeatureTest.java | 50 +++++++++++++++++ .../org/eclipse/edc/web/spi/WebService.java | 9 ++++ 19 files changed, 271 insertions(+), 31 deletions(-) create mode 100644 extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeature.java create mode 100644 extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeatureTest.java diff --git a/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/build.gradle.kts b/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/build.gradle.kts index ea483883ac8..e934dd7de90 100644 --- a/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/build.gradle.kts +++ b/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/build.gradle.kts @@ -25,7 +25,8 @@ dependencies { api(project(":spi:common:json-ld-spi")) api(project(":spi:control-plane:control-plane-spi")) - + + implementation(project(":extensions:common:http:lib:jersey-providers-lib")) implementation(project(":data-protocols:dsp:dsp-catalog:lib:dsp-catalog-validation-lib")) implementation(libs.jakarta.rsApi) diff --git a/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/src/main/java/org/eclipse/edc/protocol/dsp/catalog/http/api/DspCatalogApiExtension.java b/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/src/main/java/org/eclipse/edc/protocol/dsp/catalog/http/api/DspCatalogApiExtension.java index e2064e63e08..2bc13120f3b 100644 --- a/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/src/main/java/org/eclipse/edc/protocol/dsp/catalog/http/api/DspCatalogApiExtension.java +++ b/data-protocols/dsp/dsp-catalog/dsp-catalog-http-api/src/main/java/org/eclipse/edc/protocol/dsp/catalog/http/api/DspCatalogApiExtension.java @@ -33,16 +33,21 @@ import org.eclipse.edc.spi.query.CriterionOperatorRegistry; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.ApiContext; import static org.eclipse.edc.protocol.dsp.spi.type.DspCatalogPropertyAndTypeNames.DSPACE_TYPE_CATALOG_REQUEST_MESSAGE_IRI; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1; import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_08; import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_2024_1; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_08; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_2024_1; +import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD; /** * Creates and registers the controller for dataspace protocol catalog requests. @@ -72,7 +77,8 @@ public class DspCatalogApiExtension implements ServiceExtension { private TypeTransformerRegistry transformerRegistry; @Inject private Monitor monitor; - + @Inject + private TypeManager typeManager; @Inject private JsonLd jsonLd; @@ -84,10 +90,12 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { validatorRegistry.register(DSPACE_TYPE_CATALOG_REQUEST_MESSAGE_IRI, CatalogRequestMessageValidator.instance(criterionOperatorRegistry)); - + var jsonLdMapper = typeManager.getMapper(JSON_LD); webService.registerResource(ApiContext.PROTOCOL, new DspCatalogApiController(service, dspRequestHandler, continuationTokenManager(monitor, DSP_TRANSFORMER_CONTEXT_V_08))); webService.registerResource(ApiContext.PROTOCOL, new DspCatalogApiController20241(service, dspRequestHandler, continuationTokenManager(monitor, DSP_TRANSFORMER_CONTEXT_V_2024_1))); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspCatalogApiController.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_08)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspCatalogApiController20241.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_2024_1)); dataServiceRegistry.register(DataService.Builder.newInstance() .endpointDescription("dspace:connector") diff --git a/data-protocols/dsp/dsp-http-api-configuration/src/main/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtension.java b/data-protocols/dsp/dsp-http-api-configuration/src/main/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtension.java index 38257d06713..e5840bdfd75 100644 --- a/data-protocols/dsp/dsp-http-api-configuration/src/main/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtension.java +++ b/data-protocols/dsp/dsp-http-api-configuration/src/main/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtension.java @@ -42,7 +42,6 @@ import org.eclipse.edc.transform.transformer.edc.to.JsonObjectToCriterionTransformer; import org.eclipse.edc.transform.transformer.edc.to.JsonObjectToQuerySpecTransformer; import org.eclipse.edc.transform.transformer.edc.to.JsonValueToGenericTypeTransformer; -import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.jersey.providers.jsonld.ObjectMapperProvider; import org.eclipse.edc.web.spi.WebServer; import org.eclipse.edc.web.spi.WebService; @@ -62,7 +61,8 @@ import static org.eclipse.edc.jsonld.spi.Namespaces.DSPACE_SCHEMA; import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_PREFIX; import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; -import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1; import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_08; import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_2024_1; import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; @@ -124,16 +124,10 @@ public void initialize(ServiceExtensionContext context) { var jsonLdMapper = typeManager.getMapper(JSON_LD); // registers ns for DSP scope - jsonLd.registerNamespace(DCAT_PREFIX, DCAT_SCHEMA, DSP_SCOPE); - jsonLd.registerNamespace(DCT_PREFIX, DCT_SCHEMA, DSP_SCOPE); - jsonLd.registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, DSP_SCOPE); - jsonLd.registerNamespace(DSPACE_PREFIX, DSPACE_SCHEMA, DSP_SCOPE); - jsonLd.registerNamespace(VOCAB, EDC_NAMESPACE, DSP_SCOPE); - jsonLd.registerNamespace(EDC_PREFIX, EDC_NAMESPACE, DSP_SCOPE); - + registerNamespaces(DSP_SCOPE_V_08); + registerNamespaces(DSP_SCOPE_V_2024_1); webService.registerResource(ApiContext.PROTOCOL, new ObjectMapperProvider(jsonLdMapper)); - webService.registerResource(ApiContext.PROTOCOL, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE)); var mapper = typeManager.getMapper(JSON_LD); mapper.registerSubtypes(AtomicConstraint.class, LiteralExpression.class); @@ -142,6 +136,15 @@ public void initialize(ServiceExtensionContext context) { registerTransformers(DSP_TRANSFORMER_CONTEXT_V_2024_1, mapper); } + private void registerNamespaces(String scope) { + jsonLd.registerNamespace(DCAT_PREFIX, DCAT_SCHEMA, scope); + jsonLd.registerNamespace(DCT_PREFIX, DCT_SCHEMA, scope); + jsonLd.registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, scope); + jsonLd.registerNamespace(DSPACE_PREFIX, DSPACE_SCHEMA, scope); + jsonLd.registerNamespace(VOCAB, EDC_NAMESPACE, scope); + jsonLd.registerNamespace(EDC_PREFIX, EDC_NAMESPACE, scope); + } + private void registerTransformers(String version, ObjectMapper mapper) { var jsonBuilderFactory = Json.createBuilderFactory(Map.of()); diff --git a/data-protocols/dsp/dsp-http-api-configuration/src/test/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtensionTest.java b/data-protocols/dsp/dsp-http-api-configuration/src/test/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtensionTest.java index 1f628f7d1b4..16963829939 100644 --- a/data-protocols/dsp/dsp-http-api-configuration/src/test/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtensionTest.java +++ b/data-protocols/dsp/dsp-http-api-configuration/src/test/java/org/eclipse/edc/protocol/dsp/http/api/configuration/DspApiConfigurationExtensionTest.java @@ -23,7 +23,6 @@ import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.jersey.providers.jsonld.ObjectMapperProvider; import org.eclipse.edc.web.spi.WebServer; import org.eclipse.edc.web.spi.WebService; @@ -33,6 +32,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.util.Map; @@ -46,7 +47,8 @@ import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; import static org.eclipse.edc.protocol.dsp.http.api.configuration.DspApiConfigurationExtension.DSP_CALLBACK_ADDRESS; import static org.eclipse.edc.protocol.dsp.http.api.configuration.DspApiConfigurationExtension.SETTINGS; -import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1; import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX; import static org.mockito.ArgumentMatchers.any; @@ -116,19 +118,19 @@ void initialize_shouldRegisterWebServiceProviders(DspApiConfigurationExtension e extension.initialize(context); verify(webService).registerResource(eq(ApiContext.PROTOCOL), isA(ObjectMapperProvider.class)); - verify(webService).registerResource(eq(ApiContext.PROTOCOL), isA(JerseyJsonLdInterceptor.class)); } - @Test - void initialize_shouldRegisterNamespaces(DspApiConfigurationExtension extension, ServiceExtensionContext context) { + @ParameterizedTest + @ValueSource(strings = { DSP_SCOPE_V_08, DSP_SCOPE_V_2024_1 }) + void initialize_shouldRegisterNamespaces(String scope, DspApiConfigurationExtension extension, ServiceExtensionContext context) { extension.initialize(context); - verify(jsonLd).registerNamespace(DCAT_PREFIX, DCAT_SCHEMA, DSP_SCOPE); - verify(jsonLd).registerNamespace(DCT_PREFIX, DCT_SCHEMA, DSP_SCOPE); - verify(jsonLd).registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, DSP_SCOPE); - verify(jsonLd).registerNamespace(VOCAB, EDC_NAMESPACE, DSP_SCOPE); - verify(jsonLd).registerNamespace(EDC_PREFIX, EDC_NAMESPACE, DSP_SCOPE); - verify(jsonLd).registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, DSP_SCOPE); + verify(jsonLd).registerNamespace(DCAT_PREFIX, DCAT_SCHEMA, scope); + verify(jsonLd).registerNamespace(DCT_PREFIX, DCT_SCHEMA, scope); + verify(jsonLd).registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, scope); + verify(jsonLd).registerNamespace(VOCAB, EDC_NAMESPACE, scope); + verify(jsonLd).registerNamespace(EDC_PREFIX, EDC_NAMESPACE, scope); + verify(jsonLd).registerNamespace(ODRL_PREFIX, ODRL_SCHEMA, scope); } } diff --git a/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/DspHttpCoreExtension.java b/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/DspHttpCoreExtension.java index 96e922a2a29..1839800311d 100644 --- a/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/DspHttpCoreExtension.java +++ b/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/DspHttpCoreExtension.java @@ -148,7 +148,7 @@ public DspRequestHandler dspRequestHandler() { @Provider public JsonLdRemoteMessageSerializer jsonLdRemoteMessageSerializer() { - return new JsonLdRemoteMessageSerializerImpl(dspTransformerRegistry(), typeManager.getMapper(JSON_LD), jsonLdService, DSP_SCOPE); + return new JsonLdRemoteMessageSerializerImpl(dspTransformerRegistry(), typeManager.getMapper(JSON_LD), jsonLdService, dspProtocolParser(), DSP_SCOPE); } @Provider diff --git a/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImpl.java b/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImpl.java index 1f3b8503f90..6f046700d71 100644 --- a/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImpl.java +++ b/data-protocols/dsp/dsp-http-core/src/main/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImpl.java @@ -18,6 +18,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.JsonObject; import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser; import org.eclipse.edc.protocol.dsp.http.spi.serialization.JsonLdRemoteMessageSerializer; import org.eclipse.edc.protocol.dsp.spi.transform.DspProtocolTypeTransformerRegistry; import org.eclipse.edc.spi.EdcException; @@ -26,6 +27,7 @@ import static java.lang.String.format; import static java.lang.String.join; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_CONTEXT_SEPARATOR; /** * Serializes {@link RemoteMessage}s to JSON-LD. @@ -34,15 +36,17 @@ public class JsonLdRemoteMessageSerializerImpl implements JsonLdRemoteMessageSer private final ObjectMapper mapper; private final JsonLd jsonLdService; - private final String scope; + private final String scopePrefix; private final DspProtocolTypeTransformerRegistry dspTransformerRegistry; + private final DspProtocolParser protocolParser; public JsonLdRemoteMessageSerializerImpl(DspProtocolTypeTransformerRegistry dspTransformerRegistry, - ObjectMapper mapper, JsonLd jsonLdService, String scope) { + ObjectMapper mapper, JsonLd jsonLdService, DspProtocolParser protocolParser, String scopePrefix) { this.dspTransformerRegistry = dspTransformerRegistry; this.mapper = mapper; this.jsonLdService = jsonLdService; - this.scope = scope; + this.scopePrefix = scopePrefix; + this.protocolParser = protocolParser; } /** @@ -66,7 +70,8 @@ public String serialize(RemoteMessage message) { var transformResult = transformerRegistry.transform(message, JsonObject.class); if (transformResult.succeeded()) { - var compacted = jsonLdService.compact(transformResult.getContent(), scope); + var compacted = protocolParser.parse(message.getProtocol()) + .compose(protocol -> jsonLdService.compact(transformResult.getContent(), scopePrefix + DSP_CONTEXT_SEPARATOR + protocol.version())); if (compacted.succeeded()) { return mapper.writeValueAsString(compacted.getContent()); } diff --git a/data-protocols/dsp/dsp-http-core/src/test/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImplTest.java b/data-protocols/dsp/dsp-http-core/src/test/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImplTest.java index 043ee1dfde0..dab5ef16e88 100644 --- a/data-protocols/dsp/dsp-http-core/src/test/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImplTest.java +++ b/data-protocols/dsp/dsp-http-core/src/test/java/org/eclipse/edc/protocol/dsp/http/serialization/JsonLdRemoteMessageSerializerImplTest.java @@ -19,6 +19,7 @@ import jakarta.json.Json; import jakarta.json.JsonObject; import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser; import org.eclipse.edc.protocol.dsp.spi.transform.DspProtocolTypeTransformerRegistry; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; @@ -32,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.eclipse.edc.protocol.dsp.http.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_08; +import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_08; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; @@ -43,6 +45,7 @@ class JsonLdRemoteMessageSerializerImplTest { private final TypeTransformerRegistry registry = mock(TypeTransformerRegistry.class); private final DspProtocolTypeTransformerRegistry dspTransformerRegistry = mock(); + private final DspProtocolParser protocolParser = mock(); private final ObjectMapper mapper = mock(ObjectMapper.class); private final RemoteMessage message = mock(RemoteMessage.class); @@ -53,7 +56,7 @@ void setUp() { var jsonLdService = new TitaniumJsonLd(mock(Monitor.class)); jsonLdService.registerNamespace("schema", "http://schema/"); //needed for compaction when(registry.forContext(DSP_TRANSFORMER_CONTEXT_V_08)).thenReturn(registry); - serializer = new JsonLdRemoteMessageSerializerImpl(dspTransformerRegistry, mapper, jsonLdService, "scope"); + serializer = new JsonLdRemoteMessageSerializerImpl(dspTransformerRegistry, mapper, jsonLdService, protocolParser, "scope"); when(message.getProtocol()).thenReturn(DATASPACE_PROTOCOL_HTTP); } @@ -65,6 +68,7 @@ void serialize_shouldReturnString_whenValidMessage() throws JsonProcessingExcept when(dspTransformerRegistry.forProtocol(DATASPACE_PROTOCOL_HTTP)).thenReturn(Result.success(registry)); when(registry.transform(message, JsonObject.class)) .thenReturn(Result.success(json)); + when(protocolParser.parse(DATASPACE_PROTOCOL_HTTP)).thenReturn(Result.success(V_08)); when(mapper.writeValueAsString(any(JsonObject.class))).thenReturn(serialized); var result = serializer.serialize(message); @@ -91,6 +95,7 @@ void serialize_shouldThrowException_whenSerializationFails() throws JsonProcessi var json = messageJson(); when(dspTransformerRegistry.forProtocol(DATASPACE_PROTOCOL_HTTP)).thenReturn(Result.success(registry)); + when(protocolParser.parse(DATASPACE_PROTOCOL_HTTP)).thenReturn(Result.success(V_08)); when(registry.transform(message, JsonObject.class)) .thenReturn(Result.success(json)); when(mapper.writeValueAsString(any(JsonObject.class))).thenThrow(JsonProcessingException.class); diff --git a/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/build.gradle.kts b/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/build.gradle.kts index f74d77730b8..f131c52b8ca 100644 --- a/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/build.gradle.kts +++ b/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { api(project(":extensions:common:json-ld")) implementation(project(":data-protocols:dsp:dsp-negotiation:lib:dsp-negotiation-validation-lib")) + implementation(project(":extensions:common:http:lib:jersey-providers-lib")) implementation(libs.jakarta.rsApi) diff --git a/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/src/main/java/org/eclipse/edc/protocol/dsp/negotiation/http/api/DspNegotiationApiExtension.java b/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/src/main/java/org/eclipse/edc/protocol/dsp/negotiation/http/api/DspNegotiationApiExtension.java index 9cc6edd3d88..955951dcded 100644 --- a/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/src/main/java/org/eclipse/edc/protocol/dsp/negotiation/http/api/DspNegotiationApiExtension.java +++ b/data-protocols/dsp/dsp-negotiation/dsp-negotiation-http-api/src/main/java/org/eclipse/edc/protocol/dsp/negotiation/http/api/DspNegotiationApiExtension.java @@ -16,6 +16,7 @@ import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationProtocolService; import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersionRegistry; +import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.protocol.dsp.http.spi.message.DspRequestHandler; import org.eclipse.edc.protocol.dsp.negotiation.http.api.controller.DspNegotiationApiController; import org.eclipse.edc.protocol.dsp.negotiation.http.api.controller.DspNegotiationApiController20241; @@ -29,10 +30,14 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.ApiContext; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1; import static org.eclipse.edc.protocol.dsp.spi.type.DspNegotiationPropertyAndTypeNames.DSPACE_TYPE_CONTRACT_AGREEMENT_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.type.DspNegotiationPropertyAndTypeNames.DSPACE_TYPE_CONTRACT_AGREEMENT_VERIFICATION_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.type.DspNegotiationPropertyAndTypeNames.DSPACE_TYPE_CONTRACT_NEGOTIATION_EVENT_MESSAGE_IRI; @@ -41,6 +46,7 @@ import static org.eclipse.edc.protocol.dsp.spi.type.DspNegotiationPropertyAndTypeNames.DSPACE_TYPE_CONTRACT_REQUEST_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_08; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_2024_1; +import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD; /** * Creates and registers the controller for dataspace protocol negotiation requests. @@ -61,6 +67,12 @@ public class DspNegotiationApiExtension implements ServiceExtension { @Inject private ProtocolVersionRegistry versionRegistry; + @Inject + private JsonLd jsonLd; + + @Inject + private TypeManager typeManager; + @Override public String name() { return NAME; @@ -68,6 +80,8 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { + var jsonLdMapper = typeManager.getMapper(JSON_LD); + validatorRegistry.register(DSPACE_TYPE_CONTRACT_REQUEST_MESSAGE_IRI, ContractRequestMessageValidator.instance()); validatorRegistry.register(DSPACE_TYPE_CONTRACT_OFFER_MESSAGE_IRI, ContractOfferMessageValidator.instance()); validatorRegistry.register(DSPACE_TYPE_CONTRACT_NEGOTIATION_EVENT_MESSAGE_IRI, ContractNegotiationEventMessageValidator.instance()); @@ -77,6 +91,8 @@ public void initialize(ServiceExtensionContext context) { webService.registerResource(ApiContext.PROTOCOL, new DspNegotiationApiController(protocolService, dspRequestHandler)); webService.registerResource(ApiContext.PROTOCOL, new DspNegotiationApiController20241(protocolService, dspRequestHandler)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspNegotiationApiController.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_08)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspNegotiationApiController20241.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_2024_1)); versionRegistry.register(V_2024_1); versionRegistry.register(V_08); diff --git a/data-protocols/dsp/dsp-spi/src/main/java/org/eclipse/edc/protocol/dsp/spi/type/DspConstants.java b/data-protocols/dsp/dsp-spi/src/main/java/org/eclipse/edc/protocol/dsp/spi/type/DspConstants.java index 600f1665244..1e2504041fd 100644 --- a/data-protocols/dsp/dsp-spi/src/main/java/org/eclipse/edc/protocol/dsp/spi/type/DspConstants.java +++ b/data-protocols/dsp/dsp-spi/src/main/java/org/eclipse/edc/protocol/dsp/spi/type/DspConstants.java @@ -27,6 +27,8 @@ public interface DspConstants { String DSP_CONTEXT_SEPARATOR = ":"; String DSP_SCOPE = "DSP"; + String DSP_SCOPE_V_08 = DSP_SCOPE + DSP_CONTEXT_SEPARATOR + V_08_VERSION; + String DSP_SCOPE_V_2024_1 = DSP_SCOPE + DSP_CONTEXT_SEPARATOR + V_2024_1_VERSION; String DSP_TRANSFORMER_CONTEXT = "dsp-api"; String DSP_TRANSFORMER_CONTEXT_V_08 = DSP_TRANSFORMER_CONTEXT + DSP_CONTEXT_SEPARATOR + V_08_VERSION; String DSP_TRANSFORMER_CONTEXT_V_2024_1 = DSP_TRANSFORMER_CONTEXT + DSP_CONTEXT_SEPARATOR + V_2024_1_VERSION; diff --git a/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/build.gradle.kts b/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/build.gradle.kts index 2ad64e51bf9..6e6eddec206 100644 --- a/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/build.gradle.kts +++ b/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { implementation(project(":spi:common:json-ld-spi")) implementation(project(":data-protocols:dsp:dsp-transfer-process:lib:dsp-transfer-process-validation-lib")) + implementation(project(":extensions:common:http:lib:jersey-providers-lib")) implementation(libs.jakarta.rsApi) diff --git a/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/src/main/java/org/eclipse/edc/protocol/dsp/transferprocess/http/api/DspTransferProcessApiExtension.java b/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/src/main/java/org/eclipse/edc/protocol/dsp/transferprocess/http/api/DspTransferProcessApiExtension.java index 19f7cf84280..7b684bd297e 100644 --- a/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/src/main/java/org/eclipse/edc/protocol/dsp/transferprocess/http/api/DspTransferProcessApiExtension.java +++ b/data-protocols/dsp/dsp-transfer-process/dsp-transfer-process-http-api/src/main/java/org/eclipse/edc/protocol/dsp/transferprocess/http/api/DspTransferProcessApiExtension.java @@ -16,6 +16,7 @@ import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersionRegistry; import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessProtocolService; +import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.protocol.dsp.http.spi.message.DspRequestHandler; import org.eclipse.edc.protocol.dsp.transferprocess.http.api.controller.DspTransferProcessApiController; import org.eclipse.edc.protocol.dsp.transferprocess.http.api.controller.DspTransferProcessApiController20241; @@ -27,16 +28,21 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.ApiContext; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1; import static org.eclipse.edc.protocol.dsp.spi.type.DspTransferProcessPropertyAndTypeNames.DSPACE_TYPE_TRANSFER_COMPLETION_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.type.DspTransferProcessPropertyAndTypeNames.DSPACE_TYPE_TRANSFER_REQUEST_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.type.DspTransferProcessPropertyAndTypeNames.DSPACE_TYPE_TRANSFER_START_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.type.DspTransferProcessPropertyAndTypeNames.DSPACE_TYPE_TRANSFER_TERMINATION_MESSAGE_IRI; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_08; import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_2024_1; +import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD; /** * Creates and registers the controller for dataspace protocol transfer process requests. @@ -55,9 +61,15 @@ public class DspTransferProcessApiExtension implements ServiceExtension { private JsonObjectValidatorRegistry validatorRegistry; @Inject private ProtocolVersionRegistry versionRegistry; + @Inject + private JsonLd jsonLd; + @Inject + private TypeManager typeManager; @Override public void initialize(ServiceExtensionContext context) { + var jsonLdMapper = typeManager.getMapper(JSON_LD); + validatorRegistry.register(DSPACE_TYPE_TRANSFER_REQUEST_MESSAGE_IRI, TransferRequestMessageValidator.instance()); validatorRegistry.register(DSPACE_TYPE_TRANSFER_START_MESSAGE_IRI, TransferStartMessageValidator.instance()); validatorRegistry.register(DSPACE_TYPE_TRANSFER_COMPLETION_MESSAGE_IRI, TransferCompletionMessageValidator.instance()); @@ -65,6 +77,8 @@ public void initialize(ServiceExtensionContext context) { webService.registerResource(ApiContext.PROTOCOL, new DspTransferProcessApiController(transferProcessProtocolService, dspRequestHandler)); webService.registerResource(ApiContext.PROTOCOL, new DspTransferProcessApiController20241(transferProcessProtocolService, dspRequestHandler)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspTransferProcessApiController.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_08)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspTransferProcessApiController20241.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_2024_1)); versionRegistry.register(V_2024_1); versionRegistry.register(V_08); diff --git a/data-protocols/dsp/dsp-version/dsp-version-http-api/build.gradle.kts b/data-protocols/dsp/dsp-version/dsp-version-http-api/build.gradle.kts index 084251fd86e..70f5271ee6d 100644 --- a/data-protocols/dsp/dsp-version/dsp-version-http-api/build.gradle.kts +++ b/data-protocols/dsp/dsp-version/dsp-version-http-api/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { api(project(":spi:common:transform-spi")) api(project(":spi:common:web-spi")) api(project(":data-protocols:dsp:dsp-http-spi")) + implementation(project(":extensions:common:http:lib:jersey-providers-lib")) testImplementation(project(":core:common:junit")) testImplementation(project(":extensions:common:json-ld")) diff --git a/data-protocols/dsp/dsp-version/dsp-version-http-api/src/main/java/org/eclipse/edc/protocol/dsp/version/http/api/DspVersionApiExtension.java b/data-protocols/dsp/dsp-version/dsp-version-http-api/src/main/java/org/eclipse/edc/protocol/dsp/version/http/api/DspVersionApiExtension.java index be44d30728c..338dba314d8 100644 --- a/data-protocols/dsp/dsp-version/dsp-version-http-api/src/main/java/org/eclipse/edc/protocol/dsp/version/http/api/DspVersionApiExtension.java +++ b/data-protocols/dsp/dsp-version/dsp-version-http-api/src/main/java/org/eclipse/edc/protocol/dsp/version/http/api/DspVersionApiExtension.java @@ -16,6 +16,7 @@ import jakarta.json.Json; import org.eclipse.edc.connector.controlplane.services.spi.protocol.VersionProtocolService; +import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.protocol.dsp.http.spi.message.DspRequestHandler; import org.eclipse.edc.protocol.dsp.version.http.api.transformer.JsonObjectFromProtocolVersionsTransformer; import org.eclipse.edc.protocol.dsp.version.http.api.transformer.JsonObjectFromVersionsError; @@ -23,13 +24,17 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.web.jersey.providers.jsonld.JerseyJsonLdInterceptor; import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.ApiContext; import java.util.Map; +import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08; import static org.eclipse.edc.protocol.dsp.version.http.api.DspVersionApiExtension.NAME; +import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD; /** * Provide API for the protocol versions. @@ -51,6 +56,12 @@ public class DspVersionApiExtension implements ServiceExtension { @Inject private VersionProtocolService service; + @Inject + private JsonLd jsonLd; + @Inject + private TypeManager typeManager; + + @Override public String name() { return NAME; @@ -58,10 +69,13 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { + var jsonLdMapper = typeManager.getMapper(JSON_LD); + transformerRegistry.register(new JsonObjectFromProtocolVersionsTransformer()); transformerRegistry.register(new JsonObjectFromVersionsError(Json.createBuilderFactory(Map.of()))); webService.registerResource(ApiContext.PROTOCOL, new DspVersionApiController(requestHandler, service)); + webService.registerDynamicResource(ApiContext.PROTOCOL, DspVersionApiController.class, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, DSP_SCOPE_V_08)); } } diff --git a/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/JerseyRestService.java b/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/JerseyRestService.java index 8d847e75036..623e3a272d7 100644 --- a/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/JerseyRestService.java +++ b/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/JerseyRestService.java @@ -18,6 +18,7 @@ import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.web.jersey.feature.DynamicResourceFeature; import org.eclipse.edc.web.jersey.mapper.EdcApiExceptionMapper; import org.eclipse.edc.web.jersey.mapper.UnexpectedExceptionMapper; import org.eclipse.edc.web.jersey.providers.jsonld.ObjectMapperProvider; @@ -45,6 +46,7 @@ public class JerseyRestService implements WebService { private final Monitor monitor; private final Map> controllers = new HashMap<>(); + private final Map, List>> dynamicResources = new HashMap<>(); private final JerseyConfiguration configuration; private final List> additionalInstances = new ArrayList<>(); @@ -67,6 +69,14 @@ public void registerResource(String contextAlias, Object resource) { .add(resource); } + @Override + public void registerDynamicResource(String contextAlias, Class target, Object resource) { + dynamicResources + .computeIfAbsent(contextAlias, s -> new HashMap<>()) + .computeIfAbsent(target, s -> new ArrayList<>()) + .add(resource); + } + void registerInstance(Supplier instance) { additionalInstances.add(instance); } @@ -82,6 +92,8 @@ public void start() { private void registerContext(String contextAlias, List controllers) { var resourceConfig = new ResourceConfig(); + var dynamicResourcesForContext = dynamicResources.computeIfAbsent(contextAlias, s -> new HashMap<>()); + // Disable WADL as it is not used and emits a warning message about JAXB (which is also not used) resourceConfig.property(WADL_FEATURE_DISABLE, Boolean.TRUE); @@ -92,6 +104,7 @@ private void registerContext(String contextAlias, List controllers) { resourceConfig.registerInstances(new ObjectMapperProvider(typeManager.getMapper())); resourceConfig.registerInstances(new EdcApiExceptionMapper()); resourceConfig.registerInstances(new UnexpectedExceptionMapper(monitor)); + resourceConfig.registerInstances(new DynamicResourceFeature(dynamicResourcesForContext)); additionalInstances.forEach(supplier -> resourceConfig.registerInstances(supplier.get())); diff --git a/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeature.java b/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeature.java new file mode 100644 index 00000000000..bdf038497d1 --- /dev/null +++ b/extensions/common/http/jersey-core/src/main/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeature.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.web.jersey.feature; + +import jakarta.ws.rs.container.DynamicFeature; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.FeatureContext; + +import java.util.List; +import java.util.Map; + +import static java.util.Optional.ofNullable; + +/** + * A {@link DynamicFeature} that registers dynamic resources for a given resource class. + */ +public class DynamicResourceFeature implements DynamicFeature { + + private final Map, List> dynamicResources; + + public DynamicResourceFeature(Map, List> dynamicResources) { + this.dynamicResources = dynamicResources; + } + + @Override + public void configure(ResourceInfo resourceInfo, FeatureContext context) { + ofNullable(dynamicResources.get(resourceInfo.getResourceClass())) + .orElse(List.of()) + .forEach(context::register); + } +} diff --git a/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/JerseyRestServiceTest.java b/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/JerseyRestServiceTest.java index 6122c3942aa..1d5d3b4c09e 100644 --- a/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/JerseyRestServiceTest.java +++ b/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/JerseyRestServiceTest.java @@ -48,9 +48,9 @@ public class JerseyRestServiceTest { private final int httpPort = getFreePort(); + private final Monitor monitor = mock(Monitor.class); private JerseyRestService jerseyRestService; private JettyService jettyService; - private final Monitor monitor = mock(Monitor.class); @AfterEach void teardown() { @@ -194,6 +194,47 @@ void verifySeparateFilters() { verifyNoMoreInteractions(fooRequestFilter); } + @Test + @DisplayName("Verifies that different filters fire for different controllers") + void verifySeparateFiltersForDifferentControllers() { + var port1 = getFreePort(); + startJetty( + PortMapping.getDefault(httpPort), + new PortMapping("foo", port1, "/foo") + ); + // mocking the ContextRequestFilter doesn't work here, Mockito apparently re-uses mocks for the same target class + var testControllerFilter = mock(BarRequestFilter.class); + var bazControllerFilter = mock(FooRequestFilter.class); + + jerseyRestService.registerResource("foo", new TestController()); + jerseyRestService.registerResource("foo", new BazController()); + jerseyRestService.registerDynamicResource("foo", TestController.class, testControllerFilter); + jerseyRestService.registerDynamicResource("foo", BazController.class, bazControllerFilter); + jerseyRestService.start(); + + //verify that the first request hits only the TestController filter + given() + .get("http://localhost:" + port1 + "/foo/test/resource") + .then() + .statusCode(200); + + verify(bazControllerFilter, never()).filter(any(ContainerRequestContext.class)); + verify(testControllerFilter).filter(any(ContainerRequestContext.class)); + verifyNoMoreInteractions(testControllerFilter); + + reset(bazControllerFilter, testControllerFilter); + + // verify that the second request only hits the BazController filter + given() + .get("http://localhost:" + port1 + "/foo/baz/resource") + .then() + .statusCode(200); + + verify(testControllerFilter, never()).filter(any()); + verify(bazControllerFilter).filter(any()); + verifyNoMoreInteractions(bazControllerFilter); + } + @Test @DisplayName("Verifies that registering two identical paths raises an exception") void verifyIdenticalPathsRaiseException() { @@ -247,6 +288,17 @@ public String foo() { } } + @Produces(MediaType.TEXT_PLAIN) + @Path("/baz") + public static class BazController { //needs to be public, otherwise it won't get picked up + + @GET + @Path("/resource") + public String foo() { + return "exists"; + } + } + //needs to be public, otherwise it won't get picked up public static class BarRequestFilter implements ContainerRequestFilter { diff --git a/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeatureTest.java b/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeatureTest.java new file mode 100644 index 00000000000..979528d3e11 --- /dev/null +++ b/extensions/common/http/jersey-core/src/test/java/org/eclipse/edc/web/jersey/feature/DynamicResourceFeatureTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.web.jersey.feature; + +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.FeatureContext; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DynamicResourceFeatureTest { + + @Test + void configure() { + var feature = new DynamicResourceFeature(Map.of(Target.class, List.of(new Feature()))); + var resourceInfo = mock(ResourceInfo.class); + var featureContext = mock(FeatureContext.class); + + when(resourceInfo.getResourceClass()).then((a) -> Target.class); + + feature.configure(resourceInfo, featureContext); + + verify(featureContext).register(isA(Feature.class)); + + } + + record Feature() { + } + + record Target() { + } +} diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebService.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebService.java index 8afa02dfda1..8d5304626f1 100644 --- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebService.java +++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebService.java @@ -41,4 +41,13 @@ public interface WebService { */ void registerResource(String contextAlias, Object resource); + + /** + * Registers a dynamic resource (e.g. a filter) for a specific target class with the webservice, making it available for the default port mapping. + * + * @param target the target class of the dynamic resource + * @param resource a resource + */ + void registerDynamicResource(String contextAlias, Class target, Object resource); + }