From 45fa0800f9788db92b563e2cc6f3a150e25419a7 Mon Sep 17 00:00:00 2001
From: andrea bertagnolli <andrea.bertagnolli@gmail.com>
Date: Wed, 18 Dec 2024 14:29:49 +0100
Subject: [PATCH] refactor: introduce PortMappingRegistry service (#4677)

* refactor: introduce PortMappings service

* pr remarks
---
 .../DspApiConfigurationExtension.java         |  46 +++---
 .../DspApiConfigurationExtensionTest.java     |  26 ++--
 .../ControlApiConfigurationExtension.java     |  49 +++---
 .../ControlApiConfigurationExtensionTest.java |  21 ++-
 .../ManagementApiConfigurationExtension.java  |  50 ++++---
 ...nagementApiConfigurationExtensionTest.java |  18 +--
 .../version/VersionApiExtension.java          |  40 ++---
 .../edc/web/jersey/JerseyRestServiceTest.java |  24 +--
 .../testfixtures/RestControllerTestBase.java  |   8 +-
 .../edc/web/jetty/JettyConfiguration.java     | 108 ++------------
 .../eclipse/edc/web/jetty/JettyExtension.java |  51 +++++--
 .../eclipse/edc/web/jetty/JettyService.java   | 139 +++++++-----------
 .../eclipse/edc/web/jetty/PortMapping.java    |  62 --------
 .../web/jetty/PortMappingRegistryImpl.java    |  44 ++++++
 .../web/jetty/WebServiceConfigurerImpl.java   |  24 +--
 .../edc/web/jetty/JettyConfigurationTest.java | 135 -----------------
 .../edc/web/jetty/JettyServiceTest.java       | 110 +++-----------
 .../jetty/PortMappingRegistryImplTest.java    |  59 ++++++++
 .../jetty/WebServiceConfigurerImplTest.java   |  20 ++-
 .../StsAccountsApiConfigurationExtension.java |  43 +++---
 .../sts/StsApiConfigurationExtension.java     |  39 ++---
 .../sts/StsApiConfigurationExtensionTest.java |  24 ++-
 .../RemoteDataPlaneSelectorServiceTest.java   |   2 +
 .../api/DataPlanePublicApiV2Extension.java    |  47 +++---
 .../org/eclipse/edc/web/spi/WebServer.java    |  19 ---
 .../web/spi/configuration/PortMapping.java    |  22 +++
 .../configuration/PortMappingRegistry.java    |  38 +++++
 .../WebServiceConfiguration.java              |   3 +
 .../configuration/WebServiceConfigurer.java   |   9 +-
 .../spi/configuration/WebServiceSettings.java |   1 +
 .../configuration/WebServiceSettingsTest.java |   1 +
 .../e2e/DataPlaneSelectorEndToEndTest.java    |   2 +
 32 files changed, 548 insertions(+), 736 deletions(-)
 delete mode 100644 extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMapping.java
 create mode 100644 extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMappingRegistryImpl.java
 delete mode 100644 extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyConfigurationTest.java
 create mode 100644 extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/PortMappingRegistryImplTest.java
 create mode 100644 spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMapping.java
 create mode 100644 spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMappingRegistry.java
 rename {data-protocols/dsp/dsp-catalog/lib/dsp-catalog-http-api-lib => spi/common/web-spi}/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java (96%)

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 3d1dcf2cab9..bb5f57d4a47 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
@@ -25,11 +25,12 @@
 import org.eclipse.edc.participant.spi.ParticipantIdMapper;
 import org.eclipse.edc.policy.model.AtomicConstraint;
 import org.eclipse.edc.policy.model.LiteralExpression;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
 import org.eclipse.edc.runtime.metamodel.annotation.Provides;
 import org.eclipse.edc.runtime.metamodel.annotation.Setting;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.protocol.ProtocolWebhook;
 import org.eclipse.edc.spi.system.Hostname;
 import org.eclipse.edc.spi.system.ServiceExtension;
@@ -45,11 +46,10 @@
 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.ObjectMapperProvider;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.WebService;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 
 import java.util.Map;
 
@@ -81,25 +81,20 @@
 public class DspApiConfigurationExtension implements ServiceExtension {
 
     public static final String NAME = "Dataspace Protocol API Configuration Extension";
-    @SettingContext("Protocol API context setting key")
-    private static final String PROTOCOL_CONFIG_KEY = "web.http." + ApiContext.PROTOCOL;
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(PROTOCOL_CONFIG_KEY)
-            .contextAlias(ApiContext.PROTOCOL)
-            .defaultPath("/api/dsp")
-            .defaultPort(8282)
-            .build();
+
+    static final String DEFAULT_PROTOCOL_PATH = "/api/protocol";
+    static final int DEFAULT_PROTOCOL_PORT = 8282;
+
     @Setting(description = "Configures endpoint for reaching the Protocol API in the form \"<hostname:protocol.port/protocol.path>\"", key = "edc.dsp.callback.address", required = false)
     private String callbackAddress;
+    @Configuration
+    private DspApiConfiguration apiConfiguration;
+
     @Inject
     private TypeManager typeManager;
     @Inject
     private WebService webService;
     @Inject
-    private WebServer webServer;
-    @Inject
-    private WebServiceConfigurer configurator;
-    @Inject
     private JsonLd jsonLd;
     @Inject
     private TypeTransformerRegistry transformerRegistry;
@@ -107,6 +102,8 @@ public class DspApiConfigurationExtension implements ServiceExtension {
     private ParticipantIdMapper participantIdMapper;
     @Inject
     private Hostname hostname;
+    @Inject
+    private PortMappingRegistry portMappingRegistry;
 
     @Override
     public String name() {
@@ -115,11 +112,10 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var contextConfig = context.getConfig(PROTOCOL_CONFIG_KEY);
-        var apiConfiguration = configurator.configure(contextConfig, webServer, SETTINGS);
-        var dspWebhookAddress = ofNullable(callbackAddress).orElseGet(() -> format("http://%s:%s%s", hostname.get(), apiConfiguration.getPort(), apiConfiguration.getPath()));
-
+        var portMapping = new PortMapping(ApiContext.PROTOCOL, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
 
+        var dspWebhookAddress = ofNullable(callbackAddress).orElseGet(() -> format("http://%s:%s%s", hostname.get(), portMapping.port(), portMapping.path()));
         context.registerService(ProtocolWebhook.class, () -> dspWebhookAddress);
 
         var jsonLdMapper = typeManager.getMapper(JSON_LD);
@@ -177,7 +173,6 @@ private void registerV08Transformers(ObjectMapper mapper) {
 
         dspApiTransformerRegistry.register(new JsonObjectFromPolicyTransformer(jsonBuilderFactory, participantIdMapper));
         dspApiTransformerRegistry.register(new JsonObjectFromDataAddressDspaceTransformer(jsonBuilderFactory, mapper));
-
     }
 
     private void registerV2024Transformers(ObjectMapper mapper) {
@@ -188,6 +183,15 @@ private void registerV2024Transformers(ObjectMapper mapper) {
 
         dspApiTransformerRegistry.register(new JsonObjectFromPolicyTransformer(jsonBuilderFactory, participantIdMapper, true));
         dspApiTransformerRegistry.register(new JsonObjectFromDataAddressDspace2024Transformer(jsonBuilderFactory, mapper));
+    }
+
+    @Settings
+    record DspApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.PROTOCOL + ".port", description = "Port for " + ApiContext.PROTOCOL + " api context", defaultValue = DEFAULT_PROTOCOL_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.PROTOCOL + ".path", description = "Path for " + ApiContext.PROTOCOL + " api context", defaultValue = DEFAULT_PROTOCOL_PATH)
+            String path
+    ) {
 
     }
 }
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 6fff0136e54..308a808d077 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
@@ -20,16 +20,14 @@
 import org.eclipse.edc.spi.protocol.ProtocolWebhook;
 import org.eclipse.edc.spi.system.Hostname;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
-import org.eclipse.edc.spi.system.configuration.Config;
 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.ObjectMapperProvider;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.WebService;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -46,7 +44,8 @@
 import static org.eclipse.edc.jsonld.spi.Namespaces.DCT_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.http.api.configuration.DspApiConfigurationExtension.SETTINGS;
+import static org.eclipse.edc.protocol.dsp.http.api.configuration.DspApiConfigurationExtension.DEFAULT_PROTOCOL_PATH;
+import static org.eclipse.edc.protocol.dsp.http.api.configuration.DspApiConfigurationExtension.DEFAULT_PROTOCOL_PORT;
 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;
@@ -61,18 +60,16 @@
 @ExtendWith(DependencyInjectionExtension.class)
 class DspApiConfigurationExtensionTest {
 
-    private final WebServiceConfigurer configurer = mock();
-    private final WebServer webServer = mock();
     private final WebService webService = mock();
     private final TypeManager typeManager = mock();
     private final JsonLd jsonLd = mock();
+    private final PortMappingRegistry portMappingRegistry = mock();
 
 
     @BeforeEach
     void setUp(ServiceExtensionContext context) {
-        context.registerService(WebServer.class, webServer);
+        context.registerService(PortMappingRegistry.class, portMappingRegistry);
         context.registerService(WebService.class, webService);
-        context.registerService(WebServiceConfigurer.class, configurer);
         context.registerService(TypeManager.class, typeManager);
         context.registerService(Hostname.class, () -> "hostname");
         context.registerService(JsonLd.class, jsonLd);
@@ -80,11 +77,6 @@ void setUp(ServiceExtensionContext context) {
         when(typeTransformerRegistry.forContext(any())).thenReturn(mock());
         context.registerService(TypeTransformerRegistry.class, typeTransformerRegistry);
 
-        var webServiceConfiguration = WebServiceConfiguration.Builder.newInstance()
-                .path("/path")
-                .port(1234)
-                .build();
-        when(configurer.configure(any(Config.class), any(), any())).thenReturn(webServiceConfiguration);
         when(typeManager.getMapper(any())).thenReturn(mock());
     }
 
@@ -94,8 +86,8 @@ void shouldComposeProtocolWebhook_whenNotConfigured(DspApiConfigurationExtension
 
         extension.initialize(context);
 
-        verify(configurer).configure(any(Config.class), eq(webServer), eq(SETTINGS));
-        assertThat(context.getService(ProtocolWebhook.class).url()).isEqualTo("http://hostname:1234/path");
+        verify(portMappingRegistry).register(new PortMapping(ApiContext.PROTOCOL, DEFAULT_PROTOCOL_PORT, DEFAULT_PROTOCOL_PATH));
+        assertThat(context.getService(ProtocolWebhook.class).url()).isEqualTo("http://hostname:%s%s".formatted(DEFAULT_PROTOCOL_PORT, DEFAULT_PROTOCOL_PATH));
     }
 
     @Test
@@ -110,7 +102,7 @@ void shouldUseConfiguredProtocolWebhook(ServiceExtensionContext context, ObjectF
 
         extension.initialize(context);
 
-        verify(configurer).configure(any(Config.class), eq(webServer), eq(SETTINGS));
+        verify(portMappingRegistry).register(new PortMapping(ApiContext.PROTOCOL, 1234, "/path"));
         assertThat(context.getService(ProtocolWebhook.class).url()).isEqualTo("http://webhook");
     }
 
diff --git a/extensions/common/api/control-api-configuration/src/main/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtension.java b/extensions/common/api/control-api-configuration/src/main/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtension.java
index adee8847728..8b7abd429e7 100644
--- a/extensions/common/api/control-api-configuration/src/main/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtension.java
+++ b/extensions/common/api/control-api-configuration/src/main/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtension.java
@@ -18,11 +18,12 @@
 import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter;
 import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry;
 import org.eclipse.edc.jsonld.spi.JsonLd;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
 import org.eclipse.edc.runtime.metamodel.annotation.Provides;
 import org.eclipse.edc.runtime.metamodel.annotation.Setting;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.Hostname;
 import org.eclipse.edc.spi.system.ServiceExtension;
@@ -32,12 +33,10 @@
 import org.eclipse.edc.spi.types.TypeManager;
 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;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.context.ControlApiUrl;
 
 import java.io.IOException;
@@ -64,23 +63,18 @@
 public class ControlApiConfigurationExtension implements ServiceExtension {
 
     public static final String NAME = "Control API configuration";
+    static final String CONTROL_SCOPE = "CONTROL_API";
+    static final int DEFAULT_CONTROL_PORT = 9191;
+    static final String DEFAULT_CONTROL_PATH = "/api/control";
+    private static final String API_VERSION_JSON_FILE = "control-api-version.json";
 
     @Setting(description = "Configures endpoint for reaching the Control API. If it's missing it defaults to the hostname configuration.", key = "edc.control.endpoint", required = false)
     private String controlEndpoint;
-    public static final String CONTROL_SCOPE = "CONTROL_API";
-    @SettingContext("Control API context setting key")
-    private static final String CONTROL_CONFIG_KEY = "web.http." + ApiContext.CONTROL;
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(CONTROL_CONFIG_KEY)
-            .contextAlias(ApiContext.CONTROL)
-            .defaultPort(9191)
-            .build();
-    private static final String API_VERSION_JSON_FILE = "control-api-version.json";
+    @Configuration
+    private ControlApiConfiguration apiConfiguration;
 
     @Inject
-    private WebServer webServer;
-    @Inject
-    private WebServiceConfigurer configurator;
+    private PortMappingRegistry portMappingRegistry;
     @Inject
     private WebService webService;
     @Inject
@@ -91,7 +85,6 @@ public class ControlApiConfigurationExtension implements ServiceExtension {
     private TypeManager typeManager;
     @Inject
     private ApiAuthenticationRegistry authenticationRegistry;
-
     @Inject
     private ApiVersionService apiVersionService;
 
@@ -102,10 +95,10 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(CONTROL_CONFIG_KEY);
-        var controlApiConfiguration = configurator.configure(config, webServer, SETTINGS);
+        var portMapping = new PortMapping(ApiContext.CONTROL, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
         var jsonLdMapper = typeManager.getMapper(JSON_LD);
-        context.registerService(ControlApiUrl.class, controlApiUrl(context, controlApiConfiguration));
+        context.registerService(ControlApiUrl.class, controlApiUrl(context, portMapping));
 
         jsonLd.registerNamespace(EDC_PREFIX, EDC_NAMESPACE, CONTROL_SCOPE);
         jsonLd.registerNamespace(VOCAB, EDC_NAMESPACE, CONTROL_SCOPE);
@@ -134,8 +127,8 @@ private void registerVersionInfo(ClassLoader resourceClassLoader) {
         }
     }
 
-    private ControlApiUrl controlApiUrl(ServiceExtensionContext context, WebServiceConfiguration config) {
-        var callbackAddress = ofNullable(controlEndpoint).orElseGet(() -> format("http://%s:%s%s", hostname.get(), config.getPort(), config.getPath()));
+    private ControlApiUrl controlApiUrl(ServiceExtensionContext context, PortMapping config) {
+        var callbackAddress = ofNullable(controlEndpoint).orElseGet(() -> format("http://%s:%s%s", hostname.get(), config.port(), config.path()));
         
         try {
             var url = URI.create(callbackAddress);
@@ -145,4 +138,14 @@ private ControlApiUrl controlApiUrl(ServiceExtensionContext context, WebServiceC
             throw new EdcException(e);
         }
     }
+
+    @Settings
+    record ControlApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.CONTROL + ".port", description = "Port for " + ApiContext.CONTROL + " api context", defaultValue = DEFAULT_CONTROL_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.CONTROL + ".path", description = "Path for " + ApiContext.CONTROL + " api context", defaultValue = DEFAULT_CONTROL_PATH)
+            String path
+    ) {
+
+    }
 }
diff --git a/extensions/common/api/control-api-configuration/src/test/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtensionTest.java b/extensions/common/api/control-api-configuration/src/test/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtensionTest.java
index 0bcdd233935..269914aeb2d 100644
--- a/extensions/common/api/control-api-configuration/src/test/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtensionTest.java
+++ b/extensions/common/api/control-api-configuration/src/test/java/org/eclipse/edc/connector/api/control/configuration/ControlApiConfigurationExtensionTest.java
@@ -25,8 +25,9 @@
 import org.eclipse.edc.spi.system.configuration.ConfigFactory;
 import org.eclipse.edc.spi.types.TypeManager;
 import org.eclipse.edc.web.spi.WebService;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
+import org.eclipse.edc.web.spi.configuration.ApiContext;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.context.ControlApiUrl;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -37,6 +38,8 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.eclipse.edc.connector.api.control.configuration.ControlApiConfigurationExtension.CONTROL_SCOPE;
+import static org.eclipse.edc.connector.api.control.configuration.ControlApiConfigurationExtension.DEFAULT_CONTROL_PATH;
+import static org.eclipse.edc.connector.api.control.configuration.ControlApiConfigurationExtension.DEFAULT_CONTROL_PORT;
 import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VOCAB;
 import static org.eclipse.edc.jsonld.spi.Namespaces.DSPACE_PREFIX;
 import static org.eclipse.edc.jsonld.spi.Namespaces.DSPACE_SCHEMA;
@@ -53,24 +56,17 @@
 @ExtendWith(DependencyInjectionExtension.class)
 public class ControlApiConfigurationExtensionTest {
 
-    private final WebServiceConfigurer configurator = mock();
+    private final PortMappingRegistry portMappingRegistry = mock();
     private final WebService webService = mock();
     private final JsonLd jsonLd = mock();
 
-    private final WebServiceConfiguration webServiceConfiguration = WebServiceConfiguration.Builder.newInstance()
-            .path("/path")
-            .port(1234)
-            .build();
-
     @BeforeEach
     void setUp(ServiceExtensionContext context) {
-        context.registerService(WebServiceConfigurer.class, configurator);
+        context.registerService(PortMappingRegistry.class, portMappingRegistry);
         context.registerService(Hostname.class, () -> "hostname");
         context.registerService(WebService.class, webService);
         context.registerService(TypeManager.class, new JacksonTypeManager());
         context.registerService(JsonLd.class, jsonLd);
-
-        when(configurator.configure(any(), any(), any())).thenReturn(webServiceConfiguration);
     }
 
     @Test
@@ -79,8 +75,9 @@ void shouldComposeControlApiUrl(ControlApiConfigurationExtension extension, Serv
 
         extension.initialize(context);
 
+        verify(portMappingRegistry).register(new PortMapping(ApiContext.CONTROL, DEFAULT_CONTROL_PORT, DEFAULT_CONTROL_PATH));
         var url = context.getService(ControlApiUrl.class);
-        assertThat(url.get().toString()).isEqualTo("http://hostname:1234/path");
+        assertThat(url.get().toString()).isEqualTo("http://hostname:%s%s".formatted(DEFAULT_CONTROL_PORT, DEFAULT_CONTROL_PATH));
     }
 
     @Test
diff --git a/extensions/common/api/management-api-configuration/src/main/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtension.java b/extensions/common/api/management-api-configuration/src/main/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtension.java
index 19539ac49ca..822aa0e8c85 100644
--- a/extensions/common/api/management-api-configuration/src/main/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtension.java
+++ b/extensions/common/api/management-api-configuration/src/main/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtension.java
@@ -25,11 +25,12 @@
 import org.eclipse.edc.connector.controlplane.transform.odrl.from.JsonObjectFromPolicyTransformer;
 import org.eclipse.edc.jsonld.spi.JsonLd;
 import org.eclipse.edc.participant.spi.ParticipantIdMapper;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
 import org.eclipse.edc.runtime.metamodel.annotation.Provides;
 import org.eclipse.edc.runtime.metamodel.annotation.Setting;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.Hostname;
 import org.eclipse.edc.spi.system.ServiceExtension;
@@ -47,12 +48,10 @@
 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;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.context.ManagementApiUrl;
 
 import java.io.IOException;
@@ -79,31 +78,26 @@
 @Extension(ManagementApiConfigurationExtension.NAME)
 public class ManagementApiConfigurationExtension implements ServiceExtension {
 
-    public static final String API_VERSION_JSON_FILE = "management-api-version.json";
     public static final String NAME = "Management API configuration";
-    public static final String MANAGEMENT_SCOPE = "MANAGEMENT_API";
-
-    @SettingContext("Management API context setting key")
-    private static final String MANAGEMENT_CONFIG_KEY = "web.http." + ApiContext.MANAGEMENT;
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(MANAGEMENT_CONFIG_KEY)
-            .contextAlias(ApiContext.MANAGEMENT)
-            .defaultPort(8181)
-            .build();
+    static final String MANAGEMENT_SCOPE = "MANAGEMENT_API";
+    static final int DEFAULT_MANAGEMENT_PORT = 8181;
+    static final String DEFAULT_MANAGEMENT_PATH = "/api/management";
+    private static final String API_VERSION_JSON_FILE = "management-api-version.json";
     private static final boolean DEFAULT_MANAGEMENT_API_ENABLE_CONTEXT = false;
+
     @Setting(description = "Configures endpoint for reaching the Management API, in the format \"<hostname:management.port/management.path>\"", key = "edc.management.endpoint", required = false)
     private String managementApiEndpoint;
     @Setting(description = "If set enable the usage of management api JSON-LD context.", defaultValue = "" + DEFAULT_MANAGEMENT_API_ENABLE_CONTEXT, key = "edc.management.context.enabled")
     private boolean managementApiContextEnabled;
+    @Configuration
+    private ManagementApiConfiguration apiConfiguration;
 
     @Inject
     private WebService webService;
     @Inject
-    private WebServer webServer;
-    @Inject
     private ApiAuthenticationRegistry authenticationRegistry;
     @Inject
-    private WebServiceConfigurer configurator;
+    private PortMappingRegistry portMappingRegistry;
     @Inject
     private TypeManager typeManager;
     @Inject
@@ -125,10 +119,10 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(MANAGEMENT_CONFIG_KEY);
-        var webServiceConfiguration = configurator.configure(config, webServer, SETTINGS);
+        var portMapping = new PortMapping(ApiContext.MANAGEMENT, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
 
-        context.registerService(ManagementApiUrl.class, managementApiUrl(context, webServiceConfiguration));
+        context.registerService(ManagementApiUrl.class, managementApiUrl(context, portMapping));
 
         var authenticationFilter = new AuthenticationRequestFilter(authenticationRegistry, "management-api");
         webService.registerResource(ApiContext.MANAGEMENT, authenticationFilter);
@@ -183,8 +177,8 @@ private void registerVersionInfo(ClassLoader resourceClassLoader) {
         }
     }
 
-    private ManagementApiUrl managementApiUrl(ServiceExtensionContext context, WebServiceConfiguration config) {
-        var callbackAddress = ofNullable(managementApiEndpoint).orElseGet(() -> format("http://%s:%s%s", hostname.get(), config.getPort(), config.getPath()));
+    private ManagementApiUrl managementApiUrl(ServiceExtensionContext context, PortMapping config) {
+        var callbackAddress = ofNullable(managementApiEndpoint).orElseGet(() -> format("http://%s:%s%s", hostname.get(), config.port(), config.path()));
         try {
             var url = URI.create(callbackAddress);
             return () -> url;
@@ -193,4 +187,14 @@ private ManagementApiUrl managementApiUrl(ServiceExtensionContext context, WebSe
             throw new EdcException(e);
         }
     }
+
+    @Settings
+    record ManagementApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.MANAGEMENT + ".port", description = "Port for " + ApiContext.MANAGEMENT + " api context", defaultValue = DEFAULT_MANAGEMENT_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.MANAGEMENT + ".path", description = "Path for " + ApiContext.MANAGEMENT + " api context", defaultValue = DEFAULT_MANAGEMENT_PATH)
+            String path
+    ) {
+
+    }
 }
diff --git a/extensions/common/api/management-api-configuration/src/test/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtensionTest.java b/extensions/common/api/management-api-configuration/src/test/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtensionTest.java
index 9bf6efd0e8a..20f37b3cada 100644
--- a/extensions/common/api/management-api-configuration/src/test/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtensionTest.java
+++ b/extensions/common/api/management-api-configuration/src/test/java/org/eclipse/edc/connector/api/management/configuration/ManagementApiConfigurationExtensionTest.java
@@ -30,8 +30,8 @@
 import org.eclipse.edc.web.jersey.providers.jsonld.ObjectMapperProvider;
 import org.eclipse.edc.web.spi.WebService;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -39,8 +39,9 @@
 
 import java.util.Map;
 
+import static org.eclipse.edc.connector.api.management.configuration.ManagementApiConfigurationExtension.DEFAULT_MANAGEMENT_PATH;
+import static org.eclipse.edc.connector.api.management.configuration.ManagementApiConfigurationExtension.DEFAULT_MANAGEMENT_PORT;
 import static org.eclipse.edc.connector.api.management.configuration.ManagementApiConfigurationExtension.MANAGEMENT_SCOPE;
-import static org.eclipse.edc.connector.api.management.configuration.ManagementApiConfigurationExtension.SETTINGS;
 import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VOCAB;
 import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_PREFIX;
 import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA;
@@ -58,7 +59,7 @@
 @ExtendWith(DependencyInjectionExtension.class)
 class ManagementApiConfigurationExtensionTest {
 
-    private final WebServiceConfigurer configurer = mock();
+    private final PortMappingRegistry portMappingRegistry = mock();
     private final Monitor monitor = mock();
     private final WebService webService = mock();
     private final JsonLd jsonLd = mock();
@@ -71,7 +72,7 @@ void setUp(ServiceExtensionContext context, ObjectFactory factory) {
         when(typeTransformerRegistry.forContext(any())).thenReturn(contextTypeTransformerRegistry);
         when(contextTypeTransformerRegistry.forContext(any())).thenReturn(mock());
         context.registerService(WebService.class, webService);
-        context.registerService(WebServiceConfigurer.class, configurer);
+        context.registerService(PortMappingRegistry.class, portMappingRegistry);
         context.registerService(TypeTransformerRegistry.class, typeTransformerRegistry);
         context.registerService(TypeManager.class, new JacksonTypeManager());
         context.registerService(JsonLd.class, jsonLd);
@@ -81,12 +82,10 @@ void setUp(ServiceExtensionContext context, ObjectFactory factory) {
     @Test
     void initialize_shouldConfigureAndRegisterResource() {
         var context = contextWithConfig(ConfigFactory.empty());
-        var configuration = WebServiceConfiguration.Builder.newInstance().path("/path").port(1234).build();
-        when(configurer.configure(any(), any(), any())).thenReturn(configuration);
 
         extension.initialize(context);
 
-        verify(configurer).configure(any(), any(), eq(SETTINGS));
+        verify(portMappingRegistry).register(new PortMapping(ApiContext.MANAGEMENT, DEFAULT_MANAGEMENT_PORT, DEFAULT_MANAGEMENT_PATH));
         verify(webService).registerResource(eq(ApiContext.MANAGEMENT), isA(AuthenticationRequestFilter.class));
         verify(webService).registerResource(eq(ApiContext.MANAGEMENT), isA(JerseyJsonLdInterceptor.class));
         verify(webService).registerResource(eq(ApiContext.MANAGEMENT), isA(ObjectMapperProvider.class));
@@ -100,9 +99,6 @@ void initialize_shouldConfigureAndRegisterResource() {
     void initialize_withContextEnabled(ObjectFactory factory, ServiceExtensionContext context) {
         when(context.getConfig()).thenReturn(ConfigFactory.fromMap(Map.of("edc.management.context.enabled", "true")));
 
-        var configuration = WebServiceConfiguration.Builder.newInstance().path("/path").port(1234).build();
-        when(configurer.configure(any(), any(), any())).thenReturn(configuration);
-
         factory.constructInstance(ManagementApiConfigurationExtension.class).initialize(context);
 
         verify(jsonLd, times(0)).registerNamespace(any(), any(), any());
diff --git a/extensions/common/api/version-api/src/main/java/org/eclipse/edc/connector/api/management/version/VersionApiExtension.java b/extensions/common/api/version-api/src/main/java/org/eclipse/edc/connector/api/management/version/VersionApiExtension.java
index bb8999e4ed3..e2dc6f8effc 100644
--- a/extensions/common/api/version-api/src/main/java/org/eclipse/edc/connector/api/management/version/VersionApiExtension.java
+++ b/extensions/common/api/version-api/src/main/java/org/eclipse/edc/connector/api/management/version/VersionApiExtension.java
@@ -16,20 +16,21 @@
 
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import org.eclipse.edc.connector.api.management.version.v1.VersionApiController;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.ServiceExtension;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
 import org.eclipse.edc.spi.system.apiversion.ApiVersionService;
 import org.eclipse.edc.spi.system.apiversion.VersionRecord;
 import org.eclipse.edc.spi.types.TypeManager;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.WebService;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 
 import java.io.IOException;
 import java.util.stream.Stream;
@@ -39,17 +40,12 @@ public class VersionApiExtension implements ServiceExtension {
 
     public static final String NAME = "Management API: Version Information";
 
-    @SettingContext("Version API context setting key")
-    private static final String VERSION_CONFIG_KEY = "web.http." + ApiContext.VERSION;
-
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(VERSION_CONFIG_KEY)
-            .contextAlias(ApiContext.VERSION)
-            .defaultPath("/.well-known/api")
-            .defaultPort(7171)
-            .build();
+    private static final String DEFAULT_VERSION_PATH = "/.well-known/api";
+    private static final int DEFAULT_VERSION_PORT = 7171;
 
     private static final String API_VERSION_JSON_FILE = "version-api-version.json";
+    @Configuration
+    private VersionApiConfiguration apiConfiguration;
     @Inject
     private WebService webService;
     @Inject
@@ -57,9 +53,7 @@ public class VersionApiExtension implements ServiceExtension {
     @Inject
     private ApiVersionService apiVersionService;
     @Inject
-    private WebServiceConfigurer configurator;
-    @Inject
-    private WebServer webServer;
+    private PortMappingRegistry portMappingRegistry;
 
     @Override
     public String name() {
@@ -68,8 +62,8 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(VERSION_CONFIG_KEY);
-        configurator.configure(config, webServer, SETTINGS);
+        var portMapping = new PortMapping(ApiContext.VERSION, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
 
         webService.registerResource(ApiContext.VERSION, new VersionApiController(apiVersionService));
         registerVersionInfo(getClass().getClassLoader());
@@ -88,4 +82,14 @@ private void registerVersionInfo(ClassLoader resourceClassLoader) {
             throw new EdcException(e);
         }
     }
+
+    @Settings
+    record VersionApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.VERSION + ".port", description = "Port for " + ApiContext.VERSION + " api context", defaultValue = DEFAULT_VERSION_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.VERSION + ".path", description = "Path for " + ApiContext.VERSION + " api context", defaultValue = DEFAULT_VERSION_PATH)
+            String path
+    ) {
+
+    }
 }
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 1d5d3b4c09e..e72d0c21597 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
@@ -25,7 +25,8 @@
 import org.eclipse.edc.spi.monitor.Monitor;
 import org.eclipse.edc.web.jetty.JettyConfiguration;
 import org.eclipse.edc.web.jetty.JettyService;
-import org.eclipse.edc.web.jetty.PortMapping;
+import org.eclipse.edc.web.jetty.PortMappingRegistryImpl;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
@@ -48,7 +49,7 @@
 
 public class JerseyRestServiceTest {
     private final int httpPort = getFreePort();
-    private final Monitor monitor = mock(Monitor.class);
+    private final Monitor monitor = mock();
     private JerseyRestService jerseyRestService;
     private JettyService jettyService;
 
@@ -60,7 +61,7 @@ void teardown() {
     @Test
     @DisplayName("Verifies that a resource is available under the default path")
     void verifyDefaultContextPath() {
-        startJetty(PortMapping.getDefault(httpPort));
+        startJetty(new PortMapping("default", httpPort, "/api"));
         jerseyRestService.registerResource(new TestController());
         jerseyRestService.start();
 
@@ -76,7 +77,7 @@ void verifyDefaultContextPath() {
     void verifyAnotherContextPath() {
         var anotherPort = getFreePort();
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("path", anotherPort, "/path")
         );
         var pathController = spy(new TestController());
@@ -125,7 +126,7 @@ void verifyFilterForOneContextPath() throws IOException {
         var anotherPort = getFreePort();
         var filterMock = mock(ContainerRequestFilter.class);
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("path", anotherPort, "/path")
         );
 
@@ -158,7 +159,7 @@ void verifySeparateFilters() {
         var port1 = getFreePort();
         var port2 = getFreePort();
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("foo", port1, "/foo"),
                 new PortMapping("bar", port2, "/bar")
         );
@@ -199,7 +200,7 @@ void verifySeparateFilters() {
     void verifySeparateFiltersForDifferentControllers() {
         var port1 = getFreePort();
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("foo", port1, "/foo")
         );
         // mocking the ContextRequestFilter doesn't work here, Mockito apparently re-uses mocks for the same target class
@@ -241,7 +242,7 @@ void verifyIdenticalPathsRaiseException() {
         var port1 = getFreePort();
         var port2 = getFreePort();
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("another", port1, "/foo"),
                 new PortMapping("yet-another", port2, "/foo")
         );
@@ -258,7 +259,7 @@ void verifyIdenticalPathsRaiseException() {
     void verifyInvalidContextAlias_shouldThrowException() {
         var anotherPort = getFreePort();
         startJetty(
-                PortMapping.getDefault(httpPort),
+                new PortMapping("default", httpPort, "/api"),
                 new PortMapping("another", anotherPort, "/foo")
         );
 
@@ -270,8 +271,9 @@ void verifyInvalidContextAlias_shouldThrowException() {
 
     private void startJetty(PortMapping... mapping) {
         var config = new JettyConfiguration(null, null);
-        Arrays.stream(mapping).forEach(config::portMapping);
-        jettyService = new JettyService(config, monitor);
+        var portMappings = new PortMappingRegistryImpl();
+        Arrays.stream(mapping).forEach(portMappings::register);
+        jettyService = new JettyService(config, monitor, portMappings);
         jerseyRestService = new JerseyRestService(jettyService, new JacksonTypeManager(), JerseyConfiguration.Builder.newInstance().build(), monitor);
         jettyService.start();
     }
diff --git a/extensions/common/http/jersey-core/src/testFixtures/java/org/eclipse/edc/web/jersey/testfixtures/RestControllerTestBase.java b/extensions/common/http/jersey-core/src/testFixtures/java/org/eclipse/edc/web/jersey/testfixtures/RestControllerTestBase.java
index 692f4ea47e7..3a7a382397a 100644
--- a/extensions/common/http/jersey-core/src/testFixtures/java/org/eclipse/edc/web/jersey/testfixtures/RestControllerTestBase.java
+++ b/extensions/common/http/jersey-core/src/testFixtures/java/org/eclipse/edc/web/jersey/testfixtures/RestControllerTestBase.java
@@ -23,7 +23,8 @@
 import org.eclipse.edc.web.jersey.providers.jsonld.ObjectMapperProvider;
 import org.eclipse.edc.web.jetty.JettyConfiguration;
 import org.eclipse.edc.web.jetty.JettyService;
-import org.eclipse.edc.web.jetty.PortMapping;
+import org.eclipse.edc.web.jetty.PortMappingRegistryImpl;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
@@ -44,8 +45,9 @@ public abstract class RestControllerTestBase {
     @BeforeEach
     final void startJetty() {
         var config = new JettyConfiguration(null, null);
-        config.portMapping(new PortMapping("test", port, "/"));
-        jetty = new JettyService(config, monitor);
+        var portMappings = new PortMappingRegistryImpl();
+        portMappings.register(new PortMapping("test", port, "/"));
+        jetty = new JettyService(config, monitor, portMappings);
         var jerseyService = new JerseyRestService(jetty, new JacksonTypeManager(), mock(JerseyConfiguration.class), monitor);
         jerseyService.registerResource("test", new ObjectMapperProvider(objectMapper));
         jerseyService.registerResource("test", controller());
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyConfiguration.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyConfiguration.java
index ca4c7541419..ef6dcb315ab 100644
--- a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyConfiguration.java
+++ b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyConfiguration.java
@@ -14,102 +14,14 @@
 
 package org.eclipse.edc.web.jetty;
 
-import org.eclipse.edc.spi.system.configuration.Config;
-
-import java.util.AbstractMap;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.eclipse.edc.web.spi.configuration.WebServiceConfigurer.WEB_HTTP_PREFIX;
-
-public class JettyConfiguration {
-
-    public static final String DEFAULT_PATH = "/api";
-    public static final String DEFAULT_CONTEXT_NAME = "default";
-    public static final int DEFAULT_PORT = 8181;
-    private final String keystorePassword;
-    private final String keymanagerPassword;
-    private final Set<PortMapping> portMappings;
-
-    public JettyConfiguration(String keystorePassword, String keymanagerPassword) {
-        this.keystorePassword = keystorePassword;
-        this.keymanagerPassword = keymanagerPassword;
-        portMappings = new HashSet<>();
-    }
-
-    public static JettyConfiguration createFromConfig(String keystorePassword, String keymanagerPassword, Config config) {
-        var jettyConfig = new JettyConfiguration(keystorePassword, keymanagerPassword);
-
-        var subConfig = config.getConfig(WEB_HTTP_PREFIX);
-
-        Map<String, Map<String, String>> tempMappings = new HashMap<>();
-        subConfig.getRelativeEntries().entrySet().stream()
-                .map(e -> new AbstractMap.SimpleEntry<>(expandKey(e), e.getValue()))
-                .forEach(e -> split(tempMappings, e));
-
-        var portMappings = tempMappings.entrySet().stream()
-                .map(e -> new PortMapping(e.getKey(), Integer.parseInt(e.getValue().getOrDefault("port", "" + DEFAULT_PORT)), e.getValue().getOrDefault("path", DEFAULT_PATH)))
-                .collect(Collectors.toSet());
-
-        jettyConfig.portMappings.addAll(portMappings);
-
-
-        if (jettyConfig.getPortMappings().isEmpty()) {
-            jettyConfig.portMapping(PortMapping.getDefault());
-        }
-
-        return jettyConfig;
-    }
-
-    /**
-     * converts a map entry, that looks like "something.port" -> 1234, into a map entry, that looks like
-     * "something" -> ("port" -> "1234") and adds it to an existing map
-     */
-    private static void split(Map<String, Map<String, String>> rawMappings, Map.Entry<String, String> entry) {
-
-        var key = entry.getKey();
-        var value = entry.getValue();
-
-        // only <alias>.[port|path] is accepted
-        if (key.split("\\.").length != 2) {
-            return;
-        }
-
-        var lastDotIndex = key.lastIndexOf(".");
-        var keyNamePart = key.substring(0, lastDotIndex);
-        var keyComponentPart = key.substring(lastDotIndex + 1);
-
-        var map = rawMappings.computeIfAbsent(keyNamePart, s -> new HashMap<>());
-        if (map.containsKey(keyComponentPart)) {
-            throw new IllegalArgumentException(String.format("A port mapping for web.http.%s already exists, currently mapped to %s", key, map));
-        }
-        map.put(keyComponentPart, value);
-
-    }
-
-
-    //prepends the default context name ("default") to a key if necessary
-    private static String expandKey(Map.Entry<String, ?> entry) {
-        return entry.getKey().contains(".") ? entry.getKey() : DEFAULT_CONTEXT_NAME + "." + entry.getKey();
-    }
-
-    public Set<PortMapping> getPortMappings() {
-        return portMappings;
-    }
-
-    public void portMapping(PortMapping mapping) {
-        portMappings.add(mapping);
-    }
-
-    public String getKeystorePassword() {
-        return keystorePassword;
-    }
-
-    public String getKeymanagerPassword() {
-        return keymanagerPassword;
-    }
-
+import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
+
+@Settings
+public record JettyConfiguration(
+        @Setting(key = "edc.web.https.keystore.password", description = "Keystore password", defaultValue = "password")
+        String keystorePassword,
+        @Setting(key = "edc.web.https.keymanager.password", description = "Keymanager password", defaultValue = "password")
+        String keymanagerPassword
+) {
 }
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyExtension.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyExtension.java
index 7143632072c..7c8322c33ee 100644
--- a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyExtension.java
+++ b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyExtension.java
@@ -14,13 +14,17 @@
 
 package org.eclipse.edc.web.jetty;
 
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Provider;
 import org.eclipse.edc.runtime.metamodel.annotation.Provides;
 import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.ServiceExtension;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
 import org.eclipse.edc.web.spi.WebServer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
 
 import java.io.FileInputStream;
@@ -34,16 +38,25 @@
 public class JettyExtension implements ServiceExtension {
 
 
-    @Setting
-    private static final String KEYSTORE_PASSWORD = "edc.web.https.keystore.password";
-    @Setting
-    private static final String KEYMANAGER_PASSWORD = "edc.web.https.keymanager.password";
+    private static final String DEFAULT_PATH = "/api";
+    private static final String DEFAULT_CONTEXT_NAME = "default";
+    private static final int DEFAULT_PORT = 8181;
     @Setting
     private static final String KEYSTORE_PATH_SETTING = "edc.web.https.keystore.path";
     @Setting
     private static final String KEYSTORE_TYPE_SETTING = "edc.web.https.keystore.type";
 
     private JettyService jettyService;
+    private final PortMappingRegistryImpl portMappings = new PortMappingRegistryImpl();
+
+    @Configuration
+    private JettyConfiguration jettyConfiguration;
+    @Configuration
+    private DefaultApiConfiguration apiConfiguration;
+    @Setting(key = KEYSTORE_PATH_SETTING, description = "Keystore path", required = false)
+    private String keystorePath;
+    @Setting(key = KEYSTORE_TYPE_SETTING, description = "Keystore type", defaultValue = "PKCS12")
+    private String keystoreType;
 
     @Override
     public String name() {
@@ -52,24 +65,24 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
+        var defaultPortMapping = new PortMapping(DEFAULT_CONTEXT_NAME, apiConfiguration.port(), apiConfiguration.path());
+        portMappings.register(defaultPortMapping);
+
         var monitor = context.getMonitor();
         KeyStore ks = null;
-        var keystorePath = context.getConfig().getString(KEYSTORE_PATH_SETTING, null);
-        var configuration = JettyConfiguration.createFromConfig(context.getSetting(KEYSTORE_PASSWORD, "password"), context.getSetting(KEYMANAGER_PASSWORD, "password"), context.getConfig());
 
         if (keystorePath != null) {
             try {
-                ks = KeyStore.getInstance(context.getSetting(KEYSTORE_TYPE_SETTING, "PKCS12"));
+                ks = KeyStore.getInstance(keystoreType);
                 try (var stream = new FileInputStream(keystorePath)) {
-                    ks.load(stream, configuration.getKeystorePassword().toCharArray());
+                    ks.load(stream, jettyConfiguration.keystorePassword().toCharArray());
                 }
             } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
                 throw new EdcException(e);
             }
         }
 
-
-        jettyService = new JettyService(configuration, ks, monitor);
+        jettyService = new JettyService(jettyConfiguration, ks, monitor, portMappings);
         context.registerService(JettyService.class, jettyService);
         context.registerService(WebServer.class, jettyService);
     }
@@ -87,8 +100,24 @@ public void shutdown() {
     }
 
     @Provider
+    @Deprecated(since = "0.11.0")
     public WebServiceConfigurer webServiceContextConfigurator(ServiceExtensionContext context) {
-        return new WebServiceConfigurerImpl(context.getMonitor());
+        return new WebServiceConfigurerImpl(context.getMonitor(), portMappings);
+    }
+
+    @Provider
+    public PortMappingRegistry portMappings() {
+        return portMappings;
+    }
+
+    @Settings
+    record DefaultApiConfiguration(
+            @Setting(key = "web.http.port", description = "Port for default api context", defaultValue = DEFAULT_PORT + "")
+            int port,
+            @Setting(key = "web.http.path", description = "Path for default api context", defaultValue = DEFAULT_PATH)
+            String path
+    ) {
+
     }
 
 }
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyService.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyService.java
index 6504a6e40cd..a4a860f522f 100644
--- a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyService.java
+++ b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyService.java
@@ -21,6 +21,8 @@
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.monitor.Monitor;
 import org.eclipse.edc.web.spi.WebServer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -37,14 +39,13 @@
 
 import java.security.KeyStore;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
+import static java.util.stream.Collectors.joining;
 import static org.eclipse.jetty.servlet.ServletContextHandler.NO_SESSIONS;
 
 /**
@@ -58,16 +59,18 @@ public class JettyService implements WebServer {
     private final KeyStore keyStore;
     private final Map<String, ServletContextHandler> handlers = new HashMap<>();
     private final List<Consumer<ServerConnector>> connectorConfigurationCallbacks = new ArrayList<>();
+    private final PortMappingRegistry portMappingRegistry;
     private Server server;
 
-    public JettyService(JettyConfiguration configuration, Monitor monitor) {
-        this(configuration, null, monitor);
+    public JettyService(JettyConfiguration configuration, Monitor monitor, PortMappingRegistry portMappingRegistry) {
+        this(configuration, null, monitor, portMappingRegistry);
     }
 
-    public JettyService(JettyConfiguration configuration, KeyStore keyStore, Monitor monitor) {
+    public JettyService(JettyConfiguration configuration, KeyStore keyStore, Monitor monitor, PortMappingRegistry portMappingRegistry) {
         this.configuration = configuration;
         this.keyStore = keyStore;
         this.monitor = monitor;
+        this.portMappingRegistry = portMappingRegistry;
         System.setProperty(LOG_ANNOUNCE, "false");
         // for websocket endpoints
         handlers.put("/", new ServletContextHandler(null, "/", NO_SESSIONS));
@@ -76,37 +79,17 @@ public JettyService(JettyConfiguration configuration, KeyStore keyStore, Monitor
     public void start() {
         try {
             server = new Server();
-            //create a connector for every port mapping
-            configuration.getPortMappings().forEach(mapping -> {
-                if (!mapping.getPath().startsWith("/")) {
-                    throw new IllegalArgumentException("A context path must start with /: " + mapping.getPath());
-                }
-
-                ServerConnector connector;
-                if (Arrays.stream(server.getConnectors()).anyMatch(c -> ((ServerConnector) c).getPort() == mapping.getPort())) {
-                    throw new IllegalArgumentException("A binding for port " + mapping.getPort() + " already exists");
-                }
-
-                if (keyStore != null) {
-                    connector = httpsServerConnector(mapping.getPort());
-                    monitor.debug("HTTPS context '" + mapping.getName() + "' listening on port " + mapping.getPort());
-                } else {
-                    connector = httpServerConnector();
-                    monitor.debug("HTTP context '" + mapping.getName() + "' listening on port " + mapping.getPort());
-                }
-
-                connector.setName(mapping.getName());
-                connector.setPort(mapping.getPort());
-
-                configure(connector);
-                server.addConnector(connector);
-
-                var handler = createHandler(mapping);
-                handlers.put(mapping.getPath(), handler);
-            });
+            var portMappingsDescription = portMappingRegistry.getAll().stream()
+                    .peek(mapping -> {
+                        server.addConnector(createConnector(mapping));
+                        handlers.put(mapping.path(), createHandler(mapping));
+                    })
+                    .map(PortMapping::toString)
+                    .collect(joining(", "));
+
             server.setHandler(new ContextHandlerCollection(handlers.values().toArray(ServletContextHandler[]::new)));
             server.start();
-            monitor.debug("Port mappings: " + configuration.getPortMappings().stream().map(PortMapping::toString).collect(Collectors.joining(", ")));
+            monitor.debug("Port mappings: " + portMappingsDescription);
         } catch (Exception e) {
             throw new EdcException("Error starting Jetty service", e);
         }
@@ -130,11 +113,11 @@ public void registerServlet(String contextName, Servlet servlet) {
         servletHolder.setServlet(servlet);
         servletHolder.setInitOrder(1);
 
-        var actualPath = configuration.getPortMappings().stream()
-                .filter(pm -> Objects.equals(contextName, pm.getName()))
+        var actualPath = portMappingRegistry.getAll().stream()
+                .filter(pm -> Objects.equals(contextName, pm.name()))
                 .findFirst()
                 .orElseThrow(() -> new IllegalArgumentException("No PortMapping for contextName '" + contextName + "' found"))
-                .getPath();
+                .path();
 
         var servletHandler = getOrCreate(actualPath).getServletHandler();
         servletHandler.addServletWithMapping(servletHolder, actualPath);
@@ -143,70 +126,60 @@ public void registerServlet(String contextName, Servlet servlet) {
         servletHandler.addServletWithMapping(servletHolder, actualPath + allPathSpec);
     }
 
-    /**
-     * Allows adding a {@link PortMapping} that is not defined in the configuration. This can only
-     * be done before the JettyService is started, i.e. before {@link #start()} is called.
-     *
-     * @param contextName name of the port mapping.
-     * @param port        port of the port mapping.
-     * @param path        path of the port mapping.
-     */
-    @Override
-    public void addPortMapping(String contextName, int port, String path) {
-        var portMapping = new PortMapping(contextName, port, path);
-        if (server != null && (server.isStarted() || server.isStarting())) {
-            return;
-        }
-        configuration.getPortMappings().add(portMapping);
-    }
-
     public void addConnectorConfigurationCallback(Consumer<ServerConnector> callback) {
         connectorConfigurationCallbacks.add(callback);
     }
 
+    private @NotNull ServerConnector createConnector(PortMapping mapping) {
+        ServerConnector connector;
+        if (keyStore != null) {
+            connector = new ServerConnector(server, getSslConnectionFactory(), httpsConnectionFactory(mapping.port()));
+            monitor.debug("HTTPS context '" + mapping.name() + "' listening on port " + mapping.port());
+        } else {
+            connector = new ServerConnector(server, httpConnectionFactory());
+            monitor.debug("HTTP context '" + mapping.name() + "' listening on port " + mapping.port());
+        }
+
+        connector.setName(mapping.name());
+        connector.setPort(mapping.port());
+
+        connectorConfigurationCallbacks.forEach(c -> c.accept(connector));
+
+        return connector;
+    }
+
     @NotNull
     private ServletContextHandler createHandler(PortMapping mapping) {
         var handler = new ServletContextHandler(server, "/", NO_SESSIONS);
-        handler.setVirtualHosts(new String[]{ "@" + mapping.getName() });
+        handler.setVirtualHosts(new String[]{ "@" + mapping.name() });
         return handler;
     }
 
     @NotNull
-    private ServerConnector httpsServerConnector(int port) {
-        var storePassword = configuration.getKeystorePassword();
-        var managerPassword = configuration.getKeymanagerPassword();
-
-        // for reference check:
-        // https://medium.com/vividcode/enable-https-support-with-self-signed-certificate-for-embedded-jetty-9-d3a86f83e9d9
-        var contextFactory = new SslContextFactory.Server();
-        contextFactory.setKeyStore(keyStore);
-        contextFactory.setKeyStorePassword(storePassword);
-        contextFactory.setKeyManagerPassword(managerPassword);
+    private HttpConnectionFactory httpConnectionFactory() {
+        var httpConfiguration = new HttpConfiguration();
+        httpConfiguration.setSendServerVersion(false);
+        return new HttpConnectionFactory(httpConfiguration);
+    }
 
+    private @NotNull HttpConnectionFactory httpsConnectionFactory(int port) {
         var httpsConfiguration = new HttpConfiguration();
         httpsConfiguration.setSecureScheme("https");
         httpsConfiguration.setSecurePort(port);
         httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
-
-        var httpConnectionFactory = new HttpConnectionFactory(httpsConfiguration);
-        var sslConnectionFactory = new SslConnectionFactory(contextFactory, HttpVersion.HTTP_1_1.asString());
-        return new ServerConnector(server, sslConnectionFactory, httpConnectionFactory);
-    }
-
-    @NotNull
-    private ServerConnector httpServerConnector() {
-        return new ServerConnector(server, httpConnectionFactory());
-    }
-
-    private void configure(ServerConnector connector) {
-        connectorConfigurationCallbacks.forEach(c -> c.accept(connector));
+        return new HttpConnectionFactory(httpsConfiguration);
     }
 
-    @NotNull
-    private HttpConnectionFactory httpConnectionFactory() {
-        HttpConfiguration https = new HttpConfiguration();
-        https.setSendServerVersion(false);
-        return new HttpConnectionFactory(https);
+    private @NotNull SslConnectionFactory getSslConnectionFactory() {
+        var storePassword = configuration.keystorePassword();
+        var managerPassword = configuration.keymanagerPassword();
+        // for reference check:
+        // https://medium.com/vividcode/enable-https-support-with-self-signed-certificate-for-embedded-jetty-9-d3a86f83e9d9
+        var contextFactory = new SslContextFactory.Server();
+        contextFactory.setKeyStore(keyStore);
+        contextFactory.setKeyStorePassword(storePassword);
+        contextFactory.setKeyManagerPassword(managerPassword);
+        return new SslConnectionFactory(contextFactory, HttpVersion.HTTP_1_1.asString());
     }
 
     private ServletContextHandler getOrCreate(String contextPath) {
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMapping.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMapping.java
deleted file mode 100644
index 0b3708ea658..00000000000
--- a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMapping.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Copyright (c) 2020 - 2022 Microsoft Corporation
- *
- *  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:
- *       Microsoft Corporation - initial API and implementation
- *
- */
-
-package org.eclipse.edc.web.jetty;
-
-/**
- * POJO that contains portmappings for Jetty, consisting of a context alias, a port and a path.
- *
- * @see JettyConfiguration
- * @see JettyService
- */
-public class PortMapping {
-    private final String alias;
-    private final int port;
-    private final String path;
-
-    public static PortMapping getDefault() {
-        return getDefault(JettyConfiguration.DEFAULT_PORT);
-    }
-
-    public static PortMapping getDefault(int port) {
-        return new PortMapping(JettyConfiguration.DEFAULT_CONTEXT_NAME, port, JettyConfiguration.DEFAULT_PATH);
-    }
-
-    public PortMapping(String name, int port, String path) {
-        alias = name;
-        this.port = port;
-        this.path = path;
-    }
-
-    public String getName() {
-        return alias;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public String getPath() {
-        return path;
-    }
-
-    @Override
-    public String toString() {
-        return "{" +
-                "alias='" + alias + '\'' +
-                ", port=" + port +
-                ", path='" + path + '\'' +
-                '}';
-    }
-}
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMappingRegistryImpl.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMappingRegistryImpl.java
new file mode 100644
index 00000000000..1a69cb80e16
--- /dev/null
+++ b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/PortMappingRegistryImpl.java
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2024 Cofinity-X
+ *
+ *  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:
+ *       Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.web.jetty;
+
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PortMappingRegistryImpl implements PortMappingRegistry {
+
+    private final Map<Integer, PortMapping> portMappings = new HashMap<>();
+
+    @Override
+    public void register(PortMapping portMapping) {
+        if (!portMapping.path().startsWith("/")) {
+            throw new IllegalArgumentException("A context path must start with '/', instead it was: %s ".formatted(portMapping.path()));
+        }
+
+        if (portMappings.containsKey(portMapping.port())) {
+            throw new IllegalArgumentException("A binding for port %s already exists".formatted(portMapping.port()));
+        }
+        portMappings.put(portMapping.port(), portMapping);
+    }
+
+    @Override
+    public List<PortMapping> getAll() {
+        return portMappings.values().stream().toList();
+    }
+}
diff --git a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImpl.java b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImpl.java
index 2cffa255379..e240e1e88ef 100644
--- a/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImpl.java
+++ b/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImpl.java
@@ -17,38 +17,42 @@
 
 import org.eclipse.edc.spi.monitor.Monitor;
 import org.eclipse.edc.spi.system.configuration.Config;
-import org.eclipse.edc.web.spi.WebServer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
 import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
 import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
 
 import static java.lang.String.format;
 
+@Deprecated(since = "0.11.0")
 public class WebServiceConfigurerImpl implements WebServiceConfigurer {
 
     private final Monitor monitor;
+    private final PortMappingRegistry portMappingRegistry;
 
-    public WebServiceConfigurerImpl(Monitor monitor) {
+    public WebServiceConfigurerImpl(Monitor monitor, PortMappingRegistry portMappingRegistry) {
         this.monitor = monitor;
+        this.portMappingRegistry = portMappingRegistry;
     }
 
     @Override
-    public WebServiceConfiguration configure(Config config, WebServer webServer, WebServiceSettings settings) {
+    public WebServiceConfiguration configure(Config config, WebServiceSettings settings) {
         var apiConfig = settings.apiConfigKey();
+        var contextAlias = settings.getContextAlias();
         var port = settings.getDefaultPort();
         var path = settings.getDefaultPath();
-        var contextAlias = settings.getContextAlias();
 
-        if (!config.getEntries().isEmpty()) {
-            port = config.getInteger("port", port);
-            path = config.getString("path", path);
-        } else {
+        if (config.getEntries().isEmpty()) {
             monitor.warning("Settings for [%s] and/or [%s] were not provided. Using default value(s) instead."
                     .formatted(apiConfig + ".path", apiConfig + ".path"));
-
-            webServer.addPortMapping(contextAlias, port, path);
+        } else {
+            port = config.getInteger("port", port);
+            path = config.getString("path", path);
         }
 
+        portMappingRegistry.register(new PortMapping(contextAlias, port, path));
+
         monitor.debug(format("%s API will be available under port=%s, path=%s", contextAlias, port, path));
 
         return WebServiceConfiguration.Builder.newInstance()
diff --git a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyConfigurationTest.java b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyConfigurationTest.java
deleted file mode 100644
index d2ee29dd5bc..00000000000
--- a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyConfigurationTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- *  Copyright (c) 2020 - 2022 Microsoft Corporation
- *
- *  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:
- *       Microsoft Corporation - initial API and implementation
- *
- */
-
-package org.eclipse.edc.web.jetty;
-
-import org.assertj.core.api.ThrowableAssert;
-import org.eclipse.edc.spi.system.configuration.ConfigFactory;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-class JettyConfigurationTest {
-
-
-    @Test
-    void createFromConfig_defaultPort() {
-
-        var res = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of("web.http.port", "1234")));
-        assertThat(res.getPortMappings()).hasSize(1).allSatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("default");
-            assertThat(pm.getPort()).isEqualTo(1234);
-        });
-    }
-
-    @Test
-    void createFromConfig_noPortFound() {
-        var res = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of()));
-        assertThat(res.getPortMappings()).hasSize(1).allSatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("default");
-            assertThat(pm.getPath()).isEqualTo("/api");
-            assertThat(pm.getPort()).isEqualTo(8181);
-        });
-
-    }
-
-    @Test
-    void createFromConfig_implicitDefaultAndAnotherPort() {
-        var res = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of(
-                "web.http.port", "1234",
-                "web.http.another.port", "8888",
-                "web.http.another.path", "/foo/bar"
-        )));
-
-        assertThat(res.getPortMappings()).hasSize(2).anySatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("default");
-            assertThat(pm.getPort()).isEqualTo(1234);
-            assertThat(pm.getPath()).isEqualTo("/api");
-        }).anySatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("another");
-            assertThat(pm.getPort()).isEqualTo(8888);
-            assertThat(pm.getPath()).isEqualTo("/foo/bar");
-        });
-    }
-
-    @Test
-    void createFromConfig_explicitDefaultAndAnotherPort() {
-        var res = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of(
-                "web.http.default.port", "1234",
-                "web.http.another.port", "8888",
-                "web.http.another.path", "/foo/bar"
-        )));
-
-        assertThat(res.getPortMappings()).hasSize(2).anySatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("default");
-            assertThat(pm.getPort()).isEqualTo(1234);
-            assertThat(pm.getPath()).isEqualTo("/api");
-        }).anySatisfy(pm -> {
-            assertThat(pm.getName()).isEqualTo("another");
-            assertThat(pm.getPort()).isEqualTo(8888);
-            assertThat(pm.getPath()).isEqualTo("/foo/bar");
-        });
-    }
-
-    @Test
-    void createFromConfig_implicitAndExplicitDefault_shouldThrowException() {
-        ThrowableAssert.ThrowingCallable doubleDefaultConfig = () -> JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of(
-                "web.http.port", "1234",
-                "web.http.default.port", "8888"
-        )));
-
-        assertThatThrownBy(doubleDefaultConfig).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("A port mapping for web.http.default.port already exists, currently mapped to {port=1234}");
-    }
-
-    @Test
-    void createFromConfig_invalidAliasIsIgnored() {
-        var result = JettyConfiguration.createFromConfig(null, null,
-                ConfigFactory.fromMap(Map.of("web.http.this.is.longer.port", "8888")));
-
-        assertThat(result.getPortMappings()).allSatisfy(p -> assertThat(p).usingRecursiveComparison().isEqualTo(PortMapping.getDefault()));
-
-    }
-
-    // The exception should be thrown when starting Jetty -> Servlet already exists
-    @Test
-    void createFromConfig_multipleContextsIdenticalPath_shouldNotThrowException() {
-        var result = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of(
-                "web.http.port", "8888",
-                "web.http.path", "/foo",
-                "web.http.another.port", "1234",
-                "web.http.another.name", "test",
-                "web.http.another.path", "/foo"
-        )));
-        assertThat(result.getPortMappings()).hasSize(2).allMatch(pm -> pm.getPath().equals("/foo"));
-
-
-    }
-
-    // The exception should be thrown when starting Jetty -> failed to bind to port
-    @Test
-    void createFromConfig_multipleContextsIdenticalPort_shouldNotThrowException() {
-        var result = JettyConfiguration.createFromConfig(null, null, ConfigFactory.fromMap(Map.of(
-                "web.http.port", "8888",
-                "web.http.path", "/foo",
-                "web.http.another.port", "8888",
-                "web.http.another.name", "test",
-                "web.http.another.path", "/another"
-        )));
-        assertThat(result.getPortMappings()).hasSize(2).allMatch(pm -> pm.getPort() == 8888);
-
-    }
-}
diff --git a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyServiceTest.java b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyServiceTest.java
index cf273991a11..dcafcd18be6 100644
--- a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyServiceTest.java
+++ b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/JettyServiceTest.java
@@ -17,88 +17,66 @@
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.monitor.Monitor;
-import org.eclipse.edc.spi.system.configuration.ConfigFactory;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
-import java.net.ConnectException;
-import java.util.Map;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static io.restassured.RestAssured.given;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 class JettyServiceTest {
 
-    private JettyService jettyService;
     private final Monitor monitor = mock();
+    private final PortMappingRegistry portMappingRegistry = mock();
+    private final JettyConfiguration configuration = new JettyConfiguration(null, null);
+    private final JettyService jettyService = new JettyService(configuration, monitor, portMappingRegistry);
 
-    @Test
-    void verifyDefaultPortMapping() {
-        var config = ConfigFactory.fromMap(Map.of("web.http.port", "7171")); //default port mapping
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
-
-        jettyService.start();
-
-        jettyService.registerServlet("default", new TestServlet());
-
-        given()
-                .get("http://localhost:7171/api/test/resource")
-                .then()
-                .statusCode(200);
+    @AfterEach
+    void teardown() {
+        jettyService.shutdown();
     }
 
     @Test
-    @DisplayName("Verifies a custom port mapping")
-    void verifyCustomPortMapping() {
-        var config = ConfigFactory.fromMap(Map.of(
-                "web.http.another.port", "9191",
-                "web.http.another.path", "/another")); //default port mapping
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
+    void shouldRegisterServletOnConfiguredPortMapping() {
+        var portMapping = new PortMapping("context", 9191, "/path");
+        when(portMappingRegistry.getAll()).thenReturn(List.of(portMapping));
 
         jettyService.start();
-
-        jettyService.registerServlet("another", new TestServlet());
+        jettyService.registerServlet("context", new TestServlet());
 
         given()
-                .get("http://localhost:9191/another/test/resource")
+                .get("http://localhost:9191/path/test/resource")
                 .then()
                 .statusCode(200);
-
-        //verify that there is no default port mapping anymore
-        assertThatThrownBy(() -> given().get("http://localhost:8872/api/test/resource").then())
-                .isInstanceOf(ConnectException.class);
     }
 
     @Test
-    @DisplayName("Verifies that a custom port mapping and the implicit default mapping is possible")
-    void verifyDefaultAndCustomPortMapping() {
-        var config = ConfigFactory.fromMap(Map.of(
-                "web.http.port", "7171",
-                "web.http.another.port", "9191",
-                "web.http.another.path", "/another")); //default port mapping
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
+    void shouldConfigureMultipleApiContexts() {
+        var portMapping = new PortMapping("context", 9191, "/path");
+        var anotherPortMapping = new PortMapping("another", 9292, "/another/path");
+        when(portMappingRegistry.getAll()).thenReturn(List.of(portMapping, anotherPortMapping));
 
         jettyService.start();
-
+        jettyService.registerServlet("context", new TestServlet());
         jettyService.registerServlet("another", new TestServlet());
-        jettyService.registerServlet("default", new TestServlet());
 
         given()
-                .get("http://localhost:9191/another/test/resource")
+                .get("http://localhost:9191/path/test/resource")
                 .then()
                 .statusCode(200);
 
         given()
-                .get("http://localhost:7171/api/test/resource")
+                .get("http://localhost:9292/another/path/test/resource")
                 .then()
                 .statusCode(200);
     }
@@ -107,12 +85,10 @@ void verifyDefaultAndCustomPortMapping() {
     void verifyConnectorConfigurationCallback() {
         var listener = new JettyListener();
 
-        var config = ConfigFactory.fromMap(Map.of("web.http.port", "7171"));
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
+        when(portMappingRegistry.getAll()).thenReturn(List.of(new PortMapping("default", 7171, "/api")));
         jettyService.addConnectorConfigurationCallback((c) -> c.addBean(listener));
 
         jettyService.start();
-
         jettyService.registerServlet("default", new TestServlet());
 
         assertThat(listener.getConnectionsOpened()).isEqualTo(0);
@@ -125,13 +101,9 @@ void verifyConnectorConfigurationCallback() {
 
     @Test
     void verifyCustomPathRoot() {
-        var config = ConfigFactory.fromMap(Map.of(
-                "web.http.port", "7171",
-                "web.http.path", "/"));
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
+        when(portMappingRegistry.getAll()).thenReturn(List.of(new PortMapping("default", 7171, "/")));
 
         jettyService.start();
-
         jettyService.registerServlet("default", new TestServlet());
 
         given()
@@ -140,40 +112,6 @@ void verifyCustomPathRoot() {
                 .statusCode(200);
     }
 
-    @Test
-    void verifyInvalidPathSpecThrowsException() {
-        var config = ConfigFactory.fromMap(Map.of(
-                "web.http.port", "7171",
-                "web.http.another.port", "9191",
-                "web.http.another.path", "another")); //misses leading slash
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
-
-        assertThatThrownBy(() -> jettyService.start()).isInstanceOf(EdcException.class)
-                .hasMessage("Error starting Jetty service")
-                .hasRootCauseInstanceOf(IllegalArgumentException.class)
-                .hasRootCauseMessage("A context path must start with /: another");
-    }
-
-    @Test
-    void verifyIdenticalPorts_shouldThrowException() {
-        var config = ConfigFactory.fromMap(Map.of(
-                "web.http.first.port", "7171",
-                "web.http.first.path", "/first",
-                "web.http.another.port", "7171",
-                "web.http.another.path", "/another"));
-        jettyService = new JettyService(JettyConfiguration.createFromConfig(null, null, config), monitor);
-
-        assertThatThrownBy(() -> jettyService.start()).isInstanceOf(EdcException.class)
-                .hasMessage("Error starting Jetty service")
-                .hasRootCauseInstanceOf(IllegalArgumentException.class)
-                .hasRootCauseMessage("A binding for port 7171 already exists");
-    }
-
-    @AfterEach
-    void teardown() {
-        jettyService.shutdown();
-    }
-
     private static class JettyListener extends AbstractLifeCycle implements Connection.Listener {
 
         private final AtomicInteger connectionsOpened = new AtomicInteger();
diff --git a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/PortMappingRegistryImplTest.java b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/PortMappingRegistryImplTest.java
new file mode 100644
index 00000000000..152039456c1
--- /dev/null
+++ b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/PortMappingRegistryImplTest.java
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (c) 2024 Cofinity-X
+ *
+ *  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:
+ *       Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.web.jetty;
+
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class PortMappingRegistryImplTest {
+
+    private final PortMappingRegistryImpl portMappings = new PortMappingRegistryImpl();
+
+    @Test
+    void shouldReturnNoMappings_whenNoRegistration() {
+        assertThat(portMappings.getAll()).isEmpty();
+    }
+
+    @Test
+    void shouldReturnRegisteredMappings() {
+        var mapping = new PortMapping("name", 9292, "/path");
+        portMappings.register(mapping);
+
+        var result = portMappings.getAll();
+
+        assertThat(result).hasSize(1).containsOnly(mapping);
+    }
+
+    @Test
+    void shouldThrowException_whenMappingForPortAlreadyExist() {
+        var mapping = new PortMapping("name", 9292, "/path");
+        portMappings.register(mapping);
+
+        var invalidMapping = new PortMapping("invalid", 9292, "/invalid");
+
+        assertThatThrownBy(() -> portMappings.register(invalidMapping)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void shouldThrowException_whenPathDoesNotStartWithSlash() {
+        var mapping = new PortMapping("name", 9292, "without/trailing/slash");
+
+        assertThatThrownBy(() -> portMappings.register(mapping)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+}
diff --git a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImplTest.java b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImplTest.java
index 0eb07619990..2dc499cd788 100644
--- a/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImplTest.java
+++ b/extensions/common/http/jetty-core/src/test/java/org/eclipse/edc/web/jetty/WebServiceConfigurerImplTest.java
@@ -15,7 +15,8 @@
 package org.eclipse.edc.web.jetty;
 
 import org.eclipse.edc.spi.system.configuration.ConfigFactory;
-import org.eclipse.edc.web.spi.WebServer;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
 import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
 import org.junit.jupiter.api.Test;
@@ -24,19 +25,18 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
 
+@Deprecated(since = "0.11.0")
 public class WebServiceConfigurerImplTest {
 
     private static final String CONFIG = "web.http.test";
     private static final String PATH = "/api";
     private static final String ALIAS = "test";
     private static final int PORT = 8080;
-    private final WebServer server = mock();
+    private final PortMappingRegistry portMappingRegistry = mock();
 
-    private final WebServiceConfigurer configurator = new WebServiceConfigurerImpl(mock());
+    private final WebServiceConfigurer configurator = new WebServiceConfigurerImpl(mock(), portMappingRegistry);
 
     @Test
     void verifyConfigure_whenDefaultConfig() {
@@ -49,11 +49,9 @@ void verifyConfigure_whenDefaultConfig() {
                 .defaultPort(PORT)
                 .build();
 
-        var actualConfig = configurator.configure(config, server, settings);
-
-        verify(server, times(1))
-                .addPortMapping(ALIAS, PORT, PATH);
+        var actualConfig = configurator.configure(config, settings);
 
+        verify(portMappingRegistry).register(new PortMapping(ALIAS, PORT, PATH));
         assertThat(actualConfig.getPort()).isEqualTo(PORT);
         assertThat(actualConfig.getPath()).isEqualTo(PATH);
     }
@@ -76,9 +74,9 @@ void verifyConfigure_whenExternalConfig() {
                 .defaultPort(PORT)
                 .build();
 
-        var actualConfig = configurator.configure(config, server, settings);
+        var actualConfig = configurator.configure(config, settings);
 
-        verifyNoInteractions(server);
+        verify(portMappingRegistry).register(new PortMapping(ALIAS, port, path));
         assertThat(actualConfig.getPort()).isEqualTo(port);
         assertThat(actualConfig.getPath()).isEqualTo(path);
     }
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java
index 1ee6839b93f..0f004e5412c 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-accounts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/accounts/StsAccountsApiConfigurationExtension.java
@@ -15,19 +15,20 @@
 package org.eclipse.edc.api.iam.identitytrust.sts.accounts;
 
 import com.fasterxml.jackson.databind.DeserializationFeature;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.ServiceExtension;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
 import org.eclipse.edc.spi.system.apiversion.ApiVersionService;
 import org.eclipse.edc.spi.system.apiversion.VersionRecord;
 import org.eclipse.edc.spi.types.TypeManager;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 
 import java.io.IOException;
 import java.util.stream.Stream;
@@ -36,26 +37,18 @@
 public class StsAccountsApiConfigurationExtension implements ServiceExtension {
 
     public static final String NAME = "Secure Token Service Accounts API configuration";
-    private static final int DEFAULT_STS_ACCOUNTS_API_PORT = 9393;
-
-    @SettingContext("Sts API context setting key")
-    private static final String STS_ACCOUNTS_CONFIG_KEY = "web.http." + ApiContext.STS_ACCOUNTS;
-
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(STS_ACCOUNTS_CONFIG_KEY)
-            .contextAlias(ApiContext.STS_ACCOUNTS)
-            .defaultPort(DEFAULT_STS_ACCOUNTS_API_PORT)
-            .build();
+    private static final int DEFAULT_STS_ACCOUNTS_PORT = 9393;
+    private static final String DEFAULT_STS_ACCOUNTS_PATH = "/api/accounts";
     private static final String API_VERSION_JSON_FILE = "sts-accounts-api-version.json";
 
-    @Inject
-    private WebServer webServer;
-    @Inject
-    private WebServiceConfigurer configurator;
+    @Configuration
+    private StsAccountApiConfiguration apiConfiguration;
     @Inject
     private TypeManager typeManager;
     @Inject
     private ApiVersionService apiVersionService;
+    @Inject
+    private PortMappingRegistry portMappingRegistry;
 
     @Override
     public String name() {
@@ -64,8 +57,8 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(STS_ACCOUNTS_CONFIG_KEY);
-        configurator.configure(config, webServer, SETTINGS);
+        var portMapping = new PortMapping(ApiContext.STS_ACCOUNTS, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
         registerVersionInfo(getClass().getClassLoader());
     }
 
@@ -82,4 +75,14 @@ private void registerVersionInfo(ClassLoader resourceClassLoader) {
             throw new EdcException(e);
         }
     }
+
+    @Settings
+    record StsAccountApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.STS_ACCOUNTS + ".port", description = "Port for " + ApiContext.STS_ACCOUNTS + " api context", defaultValue = DEFAULT_STS_ACCOUNTS_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.STS_ACCOUNTS + ".path", description = "Path for " + ApiContext.STS_ACCOUNTS + " api context", defaultValue = DEFAULT_STS_ACCOUNTS_PATH)
+            String path
+    ) {
+
+    }
 }
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtension.java
index ce1fa43fa5c..4544f8335f9 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtension.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtension.java
@@ -15,19 +15,20 @@
 package org.eclipse.edc.api.iam.identitytrust.sts;
 
 import com.fasterxml.jackson.databind.DeserializationFeature;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.EdcException;
 import org.eclipse.edc.spi.system.ServiceExtension;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
 import org.eclipse.edc.spi.system.apiversion.ApiVersionService;
 import org.eclipse.edc.spi.system.apiversion.VersionRecord;
 import org.eclipse.edc.spi.types.TypeManager;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 
 import java.io.IOException;
 import java.util.stream.Stream;
@@ -36,22 +37,15 @@
 public class StsApiConfigurationExtension implements ServiceExtension {
 
     public static final String NAME = "Secure Token Service API configuration";
-    private static final int DEFAULT_STS_API_PORT = 9292;
+    static final int DEFAULT_STS_PORT = 9292;
+    static final String DEFAULT_STS_PATH = "/api/sts";
 
-    @SettingContext("Sts API context setting key")
-    private static final String STS_CONFIG_KEY = "web.http." + ApiContext.STS;
-
-    public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(STS_CONFIG_KEY)
-            .contextAlias(ApiContext.STS)
-            .defaultPort(DEFAULT_STS_API_PORT)
-            .build();
     private static final String API_VERSION_JSON_FILE = "sts-api-version.json";
 
+    @Configuration
+    private StsApiConfiguration apiConfiguration;
     @Inject
-    private WebServer webServer;
-    @Inject
-    private WebServiceConfigurer configurator;
+    private PortMappingRegistry portMappingRegistry;
     @Inject
     private TypeManager typeManager;
     @Inject
@@ -64,8 +58,7 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(STS_CONFIG_KEY);
-        configurator.configure(config, webServer, SETTINGS);
+        portMappingRegistry.register(new PortMapping(ApiContext.STS, apiConfiguration.port(), apiConfiguration.path()));
         registerVersionInfo(getClass().getClassLoader());
     }
 
@@ -82,4 +75,14 @@ private void registerVersionInfo(ClassLoader resourceClassLoader) {
             throw new EdcException(e);
         }
     }
+
+    @Settings
+    record StsApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.STS + ".port", description = "Port for " + ApiContext.STS + " api context", defaultValue = DEFAULT_STS_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.STS + ".path", description = "Path for " + ApiContext.STS + " api context", defaultValue = DEFAULT_STS_PATH)
+            String path
+    ) {
+
+    }
 }
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java
index 5aac58af969..6f29621e8c9 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/test/java/org/eclipse/edc/api/iam/identitytrust/sts/StsApiConfigurationExtensionTest.java
@@ -22,44 +22,38 @@
 import org.eclipse.edc.spi.system.configuration.Config;
 import org.eclipse.edc.spi.system.configuration.ConfigFactory;
 import org.eclipse.edc.spi.types.TypeManager;
-import org.eclipse.edc.web.spi.WebService;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
+import org.eclipse.edc.web.spi.configuration.ApiContext;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
-import static org.eclipse.edc.api.iam.identitytrust.sts.StsApiConfigurationExtension.SETTINGS;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.eclipse.edc.api.iam.identitytrust.sts.StsApiConfigurationExtension.DEFAULT_STS_PATH;
+import static org.eclipse.edc.api.iam.identitytrust.sts.StsApiConfigurationExtension.DEFAULT_STS_PORT;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 @ExtendWith(DependencyInjectionExtension.class)
 public class StsApiConfigurationExtensionTest {
 
-    private final WebServiceConfigurer configurer = mock();
-    private final Monitor monitor = mock(Monitor.class);
-    private final WebService webService = mock(WebService.class);
+    private final PortMappingRegistry portMappingRegistry = mock();
+    private final Monitor monitor = mock();
 
     @BeforeEach
     void setUp(ServiceExtensionContext context) {
-        context.registerService(WebService.class, webService);
-        context.registerService(WebServiceConfigurer.class, configurer);
+        context.registerService(PortMappingRegistry.class, portMappingRegistry);
         context.registerService(TypeManager.class, new JacksonTypeManager());
     }
 
     @Test
     void initialize_shouldConfigureAndRegisterResource(StsApiConfigurationExtension extension) {
         var context = contextWithConfig(ConfigFactory.empty());
-        var configuration = WebServiceConfiguration.Builder.newInstance().path("/path").port(1234).build();
-        when(configurer.configure(any(), any(), any())).thenReturn(configuration);
 
         extension.initialize(context);
 
-        verify(configurer).configure(any(), any(), eq(SETTINGS));
+        verify(portMappingRegistry).register(new PortMapping(ApiContext.STS, DEFAULT_STS_PORT, DEFAULT_STS_PATH));
     }
 
     @NotNull
diff --git a/extensions/data-plane-selector/data-plane-selector-client/src/test/java/org/eclipse/edc/connector/dataplane/selector/RemoteDataPlaneSelectorServiceTest.java b/extensions/data-plane-selector/data-plane-selector-client/src/test/java/org/eclipse/edc/connector/dataplane/selector/RemoteDataPlaneSelectorServiceTest.java
index a6d606f8561..dca5648521b 100644
--- a/extensions/data-plane-selector/data-plane-selector-client/src/test/java/org/eclipse/edc/connector/dataplane/selector/RemoteDataPlaneSelectorServiceTest.java
+++ b/extensions/data-plane-selector/data-plane-selector-client/src/test/java/org/eclipse/edc/connector/dataplane/selector/RemoteDataPlaneSelectorServiceTest.java
@@ -54,6 +54,7 @@ class RemoteDataPlaneSelectorServiceTest {
     public final RuntimeExtension client = new RuntimePerMethodExtension(new EmbeddedRuntime(
             "client",
             Map.of(
+                    "web.http.port", String.valueOf(getFreePort()),
                     "edc.dpf.selector.url", "http://localhost:%d/control/v1/dataplanes".formatted(port),
                     "edc.core.retry.retries.max", "0"
             ),
@@ -65,6 +66,7 @@ class RemoteDataPlaneSelectorServiceTest {
     public final RuntimeExtension server = new RuntimePerMethodExtension(new EmbeddedRuntime(
             "server",
             Map.of(
+                    "web.http.port", String.valueOf(getFreePort()),
                     "edc.dpf.selector.url", "http://not-used-but-mandatory",
                     "web.http.control.port", port + "",
                     "web.http.control.path", "/control"
diff --git a/extensions/data-plane/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java b/extensions/data-plane/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java
index 15b370191ab..ae1b9944a8b 100644
--- a/extensions/data-plane/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java
+++ b/extensions/data-plane/data-plane-public-api-v2/src/main/java/org/eclipse/edc/connector/dataplane/api/DataPlanePublicApiV2Extension.java
@@ -19,19 +19,19 @@
 import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService;
 import org.eclipse.edc.connector.dataplane.spi.iam.PublicEndpointGeneratorService;
 import org.eclipse.edc.connector.dataplane.spi.pipeline.PipelineService;
+import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
 import org.eclipse.edc.runtime.metamodel.annotation.Extension;
 import org.eclipse.edc.runtime.metamodel.annotation.Inject;
 import org.eclipse.edc.runtime.metamodel.annotation.Setting;
-import org.eclipse.edc.runtime.metamodel.annotation.SettingContext;
+import org.eclipse.edc.runtime.metamodel.annotation.Settings;
 import org.eclipse.edc.spi.system.ExecutorInstrumentation;
 import org.eclipse.edc.spi.system.Hostname;
 import org.eclipse.edc.spi.system.ServiceExtension;
 import org.eclipse.edc.spi.system.ServiceExtensionContext;
-import org.eclipse.edc.web.spi.WebServer;
 import org.eclipse.edc.web.spi.WebService;
 import org.eclipse.edc.web.spi.configuration.ApiContext;
-import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer;
-import org.eclipse.edc.web.spi.configuration.WebServiceSettings;
+import org.eclipse.edc.web.spi.configuration.PortMapping;
+import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
 
 import java.util.concurrent.Executors;
 
@@ -44,9 +44,7 @@ public class DataPlanePublicApiV2Extension implements ServiceExtension {
     public static final String NAME = "Data Plane Public API";
 
     private static final int DEFAULT_PUBLIC_PORT = 8185;
-
-    @SettingContext("Public API context setting key")
-    private static final String PUBLIC_CONFIG_KEY = "web.http." + ApiContext.PUBLIC;
+    private static final String DEFAULT_PUBLIC_PATH = "/api/public";
 
     @Setting(description = "Base url of the public API endpoint without the trailing slash. This should point to the public endpoint configured.",
             required = false,
@@ -57,33 +55,21 @@ public class DataPlanePublicApiV2Extension implements ServiceExtension {
     private String publicApiResponseUrl;
 
     private static final int DEFAULT_THREAD_POOL = 10;
-    private static final WebServiceSettings PUBLIC_SETTINGS = WebServiceSettings.Builder.newInstance()
-            .apiConfigKey(PUBLIC_CONFIG_KEY)
-            .contextAlias(ApiContext.PUBLIC)
-            .defaultPort(DEFAULT_PUBLIC_PORT)
-            .build();
-
-    @Inject
-    private WebServer webServer;
 
+    @Configuration
+    private PublicApiConfiguration apiConfiguration;
     @Inject
-    private WebServiceConfigurer webServiceConfigurer;
-
+    private PortMappingRegistry portMappingRegistry;
     @Inject
     private PipelineService pipelineService;
-
     @Inject
     private WebService webService;
-
     @Inject
     private ExecutorInstrumentation executorInstrumentation;
-
     @Inject
     private DataPlaneAuthorizationService authorizationService;
-
     @Inject
     private PublicEndpointGeneratorService generatorService;
-
     @Inject
     private Hostname hostname;
 
@@ -94,16 +80,15 @@ public String name() {
 
     @Override
     public void initialize(ServiceExtensionContext context) {
-        var config = context.getConfig(PUBLIC_CONFIG_KEY);
-        var configuration = webServiceConfigurer.configure(config, webServer, PUBLIC_SETTINGS);
+        var portMapping = new PortMapping(ApiContext.PUBLIC, apiConfiguration.port(), apiConfiguration.path());
+        portMappingRegistry.register(portMapping);
         var executorService = executorInstrumentation.instrument(
                 Executors.newFixedThreadPool(DEFAULT_THREAD_POOL),
                 "Data plane proxy transfers"
         );
 
-
         if (publicBaseUrl == null) {
-            publicBaseUrl = "http://%s:%d%s".formatted(hostname.get(), configuration.getPort(), configuration.getPath());
+            publicBaseUrl = "http://%s:%d%s".formatted(hostname.get(), portMapping.port(), portMapping.path());
             context.getMonitor().warning("The public API endpoint was not explicitly configured, the default '%s' will be used.".formatted(publicBaseUrl));
         }
         var endpoint = Endpoint.url(publicBaseUrl);
@@ -116,4 +101,14 @@ public void initialize(ServiceExtensionContext context) {
         var publicApiController = new DataPlanePublicApiV2Controller(pipelineService, executorService, authorizationService);
         webService.registerResource(ApiContext.PUBLIC, publicApiController);
     }
+
+    @Settings
+    record PublicApiConfiguration(
+            @Setting(key = "web.http." + ApiContext.PUBLIC + ".port", description = "Port for " + ApiContext.PUBLIC + " api context", defaultValue = DEFAULT_PUBLIC_PORT + "")
+            int port,
+            @Setting(key = "web.http." + ApiContext.PUBLIC + ".path", description = "Path for " + ApiContext.PUBLIC + " api context", defaultValue = DEFAULT_PUBLIC_PATH)
+            String path
+    ) {
+
+    }
 }
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebServer.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebServer.java
index 31f42528a51..53e6746a143 100644
--- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebServer.java
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/WebServer.java
@@ -23,18 +23,6 @@
 @ExtensionPoint
 public interface WebServer {
 
-    String DEFAULT_CONTEXT_NAME = "default";
-
-
-    /**
-     * Adds a new port mapping and thus a new API context to this web server.
-     *
-     * @param contextName the name of the API context.
-     * @param port        the port of the API context.
-     * @param path        the path of the API context.
-     */
-    void addPortMapping(String contextName, int port, String path);
-
     /**
      * Adds a new servlet to the specified context name..
      *
@@ -43,11 +31,4 @@ public interface WebServer {
      */
     void registerServlet(String contextName, Servlet servlet);
 
-    /**
-     * Returns the default context name
-     */
-    default String getDefaultContextName() {
-        return DEFAULT_CONTEXT_NAME;
-    }
-
 }
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMapping.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMapping.java
new file mode 100644
index 00000000000..0f41f2f942f
--- /dev/null
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMapping.java
@@ -0,0 +1,22 @@
+/*
+ *  Copyright (c) 2024 Cofinity-X
+ *
+ *  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:
+ *       Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.web.spi.configuration;
+
+/**
+ * POJO that contains port mappings for api context, consisting of a context alias, a port and a path.
+ */
+public record PortMapping(String name, int port, String path) {
+
+}
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMappingRegistry.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMappingRegistry.java
new file mode 100644
index 00000000000..487c0bc4fa6
--- /dev/null
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/PortMappingRegistry.java
@@ -0,0 +1,38 @@
+/*
+ *  Copyright (c) 2024 Cofinity-X
+ *
+ *  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:
+ *       Cofinity-X - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.web.spi.configuration;
+
+import java.util.List;
+
+/**
+ * PortMapping registry.
+ */
+public interface PortMappingRegistry {
+
+    /**
+     * Register a PortMapping.
+     *
+     * @param portMapping the port mapping.
+     */
+    void register(PortMapping portMapping);
+
+    /**
+     * Return all the registered port mapping.
+     *
+     * @return all the port mappings registered.
+     */
+    List<PortMapping> getAll();
+
+}
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfiguration.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfiguration.java
index 69316f4c9d9..04d7a8f96bb 100644
--- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfiguration.java
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfiguration.java
@@ -18,7 +18,10 @@
 
 /**
  * WebService configuration returned from {@link WebServiceConfigurer}
+ *
+ * @deprecated please use {@link PortMapping}.
  */
+@Deprecated(since = "0.11.0")
 public class WebServiceConfiguration {
 
     private Integer port;
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfigurer.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfigurer.java
index 13360625653..0274750dd6d 100644
--- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfigurer.java
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceConfigurer.java
@@ -17,12 +17,14 @@
 
 import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint;
 import org.eclipse.edc.spi.system.configuration.Config;
-import org.eclipse.edc.web.spi.WebServer;
 
 /**
  * Configure an API extension
+ *
+ * @deprecated please use {@link PortMappingRegistry} service.
  */
 @ExtensionPoint
+@Deprecated(since = "0.11.0")
 public interface WebServiceConfigurer {
 
     String WEB_HTTP_PREFIX = "web.http";
@@ -31,9 +33,10 @@ public interface WebServiceConfigurer {
      * Build the configuration for an API
      *
      * @param config    The context configuration
-     * @param webServer The WebServer
      * @param settings  WebService settings
      * @return The final webservice configuration
+     * @deprecated please use {@link PortMappingRegistry}
      */
-    WebServiceConfiguration configure(Config config, WebServer webServer, WebServiceSettings settings);
+    @Deprecated(since = "0.11.0")
+    WebServiceConfiguration configure(Config config, WebServiceSettings settings);
 }
diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceSettings.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceSettings.java
index 01e2ed47c57..ecbda46da79 100644
--- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceSettings.java
+++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/configuration/WebServiceSettings.java
@@ -16,6 +16,7 @@
 
 import java.util.Objects;
 
+@Deprecated(since = "0.11.0")
 public class WebServiceSettings {
 
     private String apiConfigKey;
diff --git a/data-protocols/dsp/dsp-catalog/lib/dsp-catalog-http-api-lib/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java b/spi/common/web-spi/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java
similarity index 96%
rename from data-protocols/dsp/dsp-catalog/lib/dsp-catalog-http-api-lib/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java
rename to spi/common/web-spi/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java
index 7cbface6429..d85e8209b3a 100644
--- a/data-protocols/dsp/dsp-catalog/lib/dsp-catalog-http-api-lib/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java
+++ b/spi/common/web-spi/src/test/java/org/eclipse/edc/web/spi/configuration/WebServiceSettingsTest.java
@@ -18,6 +18,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+@Deprecated(since = "0.11.0")
 class WebServiceSettingsTest {
 
     @Test
diff --git a/system-tests/e2e-dataplane-tests/tests/src/test/java/org/eclipse/edc/test/e2e/DataPlaneSelectorEndToEndTest.java b/system-tests/e2e-dataplane-tests/tests/src/test/java/org/eclipse/edc/test/e2e/DataPlaneSelectorEndToEndTest.java
index f9162e574ae..4be415a24e8 100644
--- a/system-tests/e2e-dataplane-tests/tests/src/test/java/org/eclipse/edc/test/e2e/DataPlaneSelectorEndToEndTest.java
+++ b/system-tests/e2e-dataplane-tests/tests/src/test/java/org/eclipse/edc/test/e2e/DataPlaneSelectorEndToEndTest.java
@@ -48,6 +48,7 @@ public class DataPlaneSelectorEndToEndTest {
     private final RuntimeExtension controlPlane = new RuntimePerMethodExtension(new EmbeddedRuntime(
             "control-plane",
             Map.of(
+                    "web.http.port", String.valueOf(getFreePort()),
                     "web.http.control.port", String.valueOf(getFreePort()),
                     "web.http.control.path", "/control",
                     "edc.dataplane.client.selector.strategy", SELECT_FIRST,
@@ -70,6 +71,7 @@ public class DataPlaneSelectorEndToEndTest {
     private final RuntimeExtension dataPlane = new RuntimePerMethodExtension(new EmbeddedRuntime(
             "data-plane",
             Map.of(
+                    "web.http.port", String.valueOf(getFreePort()),
                     "web.http.control.port", String.valueOf(dataPlaneControlPort),
                     "web.http.control.path", "/control"
             ),