Allow Feature and Dynamic feature as a JDK services (#4833)

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
index 0979ec6..89e87f1 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
@@ -43,6 +43,7 @@
 import org.glassfish.jersey.internal.BootstrapConfigurator;
 import org.glassfish.jersey.internal.ContextResolverFactory;
 import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.glassfish.jersey.internal.FeatureConfigurator;
 import org.glassfish.jersey.internal.JaxrsProviders;
 import org.glassfish.jersey.internal.ServiceFinder;
 import org.glassfish.jersey.internal.inject.Bindings;
@@ -53,7 +54,6 @@
 import org.glassfish.jersey.internal.util.collection.LazyValue;
 import org.glassfish.jersey.internal.util.collection.Value;
 import org.glassfish.jersey.internal.util.collection.Values;
-import org.glassfish.jersey.message.internal.MessageBodyFactory;
 import org.glassfish.jersey.model.internal.CommonConfig;
 import org.glassfish.jersey.model.internal.ComponentBag;
 import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
@@ -429,7 +429,8 @@
                     new ExceptionMapperFactory.ExceptionMappersConfigurator(),
                     new JaxrsProviders.ProvidersConfigurator(),
                     new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
-                    new ClientComponentConfigurator());
+                    new ClientComponentConfigurator(),
+                    new FeatureConfigurator(RuntimeType.CLIENT));
             bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
 
             // AutoDiscoverable.
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/JaxRsFeatureRegistrationClientTest.java b/core-client/src/test/java/org/glassfish/jersey/client/JaxRsFeatureRegistrationClientTest.java
new file mode 100644
index 0000000..8bfb921
--- /dev/null
+++ b/core-client/src/test/java/org/glassfish/jersey/client/JaxRsFeatureRegistrationClientTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.client;
+
+import org.glassfish.jersey.CommonProperties;
+import org.junit.Test;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class JaxRsFeatureRegistrationClientTest {
+
+    private static final String REGISTERED_FEATURE_RESPONSE = "RegisteredFeature";
+
+    private static final ClientRequestFilter component =
+            requestContext -> requestContext
+                    .abortWith(Response.status(400).entity(REGISTERED_FEATURE_RESPONSE).build());
+
+    public static class FeatureClientImpl implements Feature {
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            if ("true".equals(context.getConfiguration().getProperty("runWithJaxRsClient"))) {
+                context.register(component);
+            }
+            return true;
+        }
+    }
+
+    @Test
+    public void featureRegistrationTest() {
+        final ClientConfig config = new ClientConfig().property("runWithJaxRsClient", "true");
+        final Client client = ClientBuilder.newClient(config);
+        final Invocation.Builder request = client.target("").request();
+
+        assertEquals(REGISTERED_FEATURE_RESPONSE, request.get().readEntity(String.class));
+
+        client.close();
+    }
+
+    @Test
+    public void featureNotRegistrationTest() {
+        final ClientConfig config = new ClientConfig()
+                .property(CommonProperties.JAXRS_SERVICE_LOADING_ENABLE, false);
+        final Client client = ClientBuilder.newClient(config);
+        final Invocation.Builder request = client.target("").request();
+
+        assertFalse(client.getConfiguration().isRegistered(FeatureClientImpl.class));
+
+        client.close();
+    }
+
+}
\ No newline at end of file
diff --git a/core-client/src/test/resources/META-INF/services/javax.ws.rs.core.Feature b/core-client/src/test/resources/META-INF/services/javax.ws.rs.core.Feature
new file mode 100644
index 0000000..99c6b86
--- /dev/null
+++ b/core-client/src/test/resources/META-INF/services/javax.ws.rs.core.Feature
@@ -0,0 +1 @@
+org.glassfish.jersey.client.JaxRsFeatureRegistrationClientTest$FeatureClientImpl
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java
new file mode 100644
index 0000000..dbc4308
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey;
+
+import org.glassfish.jersey.internal.AbstractServiceFinderConfigurator;
+import org.glassfish.jersey.internal.BootstrapBag;
+import org.glassfish.jersey.internal.ServiceFinder;
+import org.glassfish.jersey.internal.spi.AutoDiscoverable;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+import javax.ws.rs.RuntimeType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public abstract class AbstractFeatureConfigurator<T> extends AbstractServiceFinderConfigurator<T> {
+    /**
+     * Create a new configurator.
+     *
+     * @param contract    contract of the service providers bound by this binder.
+     * @param runtimeType runtime (client or server) where the service finder binder is used.
+     */
+    protected AbstractFeatureConfigurator(Class contract, RuntimeType runtimeType) {
+        super(contract, runtimeType);
+    }
+
+    /**
+     * Specification specific implementation which allows find classes by specified classloader
+     *
+     * @param applicationProperties map of properties to check if search is allowed
+     * @param loader specific classloader (must not be NULL)
+     * @return list of found classes
+     */
+    protected List<Class<T>> loadImplementations(Map<String, Object> applicationProperties, ClassLoader loader) {
+        if (PropertiesHelper.isMetaInfServicesEnabled(applicationProperties, getRuntimeType())) {
+            return Stream.of(ServiceFinder.find(getContract(), loader, true).toClassArray())
+                    .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Allows feature registration as part of autoDiscoverables list
+     *
+     * @param features list of features to be registered
+     * @param bootstrapBag place where features are being registered
+     */
+    protected void registerFeatures(List<Class<T>> features,
+                                    BootstrapBag bootstrapBag) {
+        final List<AutoDiscoverable> autoDiscoverables = new ArrayList<>();
+
+        features.forEach(feature -> autoDiscoverables.add(registerClass(feature)));
+
+        bootstrapBag.getAutoDiscoverables().addAll(autoDiscoverables);
+    }
+
+    /**
+     * Register particular feature as an autoDiscoverable
+     *
+     * @param classToRegister class to be registered
+     * @param <T> type of class which is being registered
+     * @return initialized autoDiscoverable
+     */
+    private static <T> AutoDiscoverable registerClass(Class<T> classToRegister) {
+        return context -> {
+            if (!context.getConfiguration().isRegistered(classToRegister)) {
+                context.register(classToRegister, AutoDiscoverable.DEFAULT_PRIORITY);
+            }
+        };
+    }
+
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
index 94c54ed..50fceb1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -245,6 +245,16 @@
      */
     public static final String PROVIDER_DEFAULT_DISABLE = "jersey.config.disableDefaultProvider";
 
+
+    /**
+     * Allows API services loading. If absent or true JAXRS services loading is allowed.
+     * Services shall implement Feature or DynamicFeature interface and be listed as SPI
+     * in user's application.
+     *
+     * @since 2.35
+     */
+    public static final String JAXRS_SERVICE_LOADING_ENABLE = "jakarta.ws.rs.loadServices";
+
     /**
      * Prevent instantiation.
      */
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
index b835b55..b528eaf 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -65,4 +65,22 @@
         }
         return Collections.emptyList();
     }
+
+    /**
+     * Mainly aimed to provide runtime type to abstract classes which extends this finder
+     *
+     * @return runtime type
+     */
+    protected RuntimeType getRuntimeType() {
+        return runtimeType;
+    }
+
+    /**
+     * Mainly aimed to provide contract class to abstract classes which extends this finder
+     *
+     * @return contract class
+     */
+    protected Class<T> getContract() {
+        return contract;
+    }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
new file mode 100644
index 0000000..ed2c8fb
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal;
+
+import org.glassfish.jersey.AbstractFeatureConfigurator;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.container.DynamicFeature;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Registers JAX-RS {@link DynamicFeature} which are listed as SPIs for registration.
+ * Also checks if JAX-RS service loading is enabled by the jakarta.ws.rs.loadServices property. In order for
+ * registration to proceed the property shall be true (or null).
+ *
+ * Configurator is used only at Server side.
+ *
+ * This configurator's instance shall be after {@link AutoDiscoverableConfigurator}
+ * in the list of configurators due to same list of {@link org.glassfish.jersey.internal.spi.AutoDiscoverable}
+ * used in the {@link BootstrapBag} to register discovered features.
+ */
+public class DynamicFeatureConfigurator extends AbstractFeatureConfigurator<DynamicFeature> {
+
+    /**
+     * Create a new configurator.
+     *
+     * Must be used at server side only (takes no effect as a client).
+     */
+    public DynamicFeatureConfigurator() {
+        super(DynamicFeature.class, RuntimeType.SERVER);
+    }
+
+    @Override
+    public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
+        final Map<String, Object> properties = bootstrapBag.getConfiguration().getProperties();
+        if (PropertiesHelper.isJaxRsServiceLoadingEnabled(properties)) {
+            final List<Class<DynamicFeature>> dynamicFeatures = loadImplementations(properties);
+            dynamicFeatures.addAll(loadImplementations(properties, DynamicFeature.class.getClassLoader()));
+
+            registerFeatures(dynamicFeatures, bootstrapBag);
+        }
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/FeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/FeatureConfigurator.java
new file mode 100644
index 0000000..24a24fb
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/FeatureConfigurator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal;
+
+import org.glassfish.jersey.AbstractFeatureConfigurator;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.core.Feature;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Registers JAX-RS {@link Feature} which are listed as SPIs for registration.
+ * Also checks if JAX-RS service loading is enabled by the jakarta.ws.rs.loadServices property. In order for
+ * registration to proceed the property shall be true (or null).
+ *
+ * This configurator's instance shall be the last (or at least after {@link AutoDiscoverableConfigurator})
+ * in the list of configurators due to same list of {@link org.glassfish.jersey.internal.spi.AutoDiscoverable}
+ * used in the {@link BootstrapBag} to register discovered features.
+ */
+public class FeatureConfigurator extends AbstractFeatureConfigurator<Feature> {
+
+    public FeatureConfigurator(RuntimeType runtimeType) {
+        super(Feature.class, runtimeType);
+    }
+
+    @Override
+    public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
+        final Map<String, Object> properties = bootstrapBag.getConfiguration().getProperties();
+        if (PropertiesHelper.isJaxRsServiceLoadingEnabled(properties)) {
+            final List<Class<Feature>> features = loadImplementations(properties);
+            features.addAll(loadImplementations(properties, Feature.class.getClassLoader()));
+
+            registerFeatures(features, bootstrapBag);
+        }
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
index dcea2d3..4e6aace 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -41,6 +41,7 @@
 
     private static final Logger LOGGER = Logger.getLogger(PropertiesHelper.class.getName());
     private static final boolean METAINF_SERVICES_LOOKUP_DISABLE_DEFAULT = false;
+    private static final boolean JAXRS_SERVICE_LOADING_ENABLE_DEFAULT = true;
 
     /**
      * Get system properties.
@@ -334,6 +335,24 @@
     }
 
     /**
+     * Check whether loading of JAX-RS services is allowed or not. Services shall implement Feature or DynamicFeature
+     * interface and be listed as SPI in an application
+     *
+     * @param properties list of properties to be checked
+     * @return false if loading of JAX-RS services is not enabled
+     *
+     * @since 2.35
+     */
+    public static boolean isJaxRsServiceLoadingEnabled(Map<String, Object> properties) {
+        boolean enableServicesLoading = JAXRS_SERVICE_LOADING_ENABLE_DEFAULT;
+        if (properties != null) {
+            enableServicesLoading = CommonProperties.getValue(properties,
+                    CommonProperties.JAXRS_SERVICE_LOADING_ENABLE, JAXRS_SERVICE_LOADING_ENABLE_DEFAULT);
+        }
+        return enableServicesLoading;
+    }
+
+    /**
      * Get the value of the property with a given name converted to {@code boolean}. Returns {@code false} if the value is
      * not convertible.
      *
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
index 4239402..2e0447f 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates.
  *
  * This program and the accompanying materials are made available under the
@@ -50,8 +50,10 @@
 import org.glassfish.jersey.internal.BootstrapBag;
 import org.glassfish.jersey.internal.BootstrapConfigurator;
 import org.glassfish.jersey.internal.ContextResolverFactory;
+import org.glassfish.jersey.internal.DynamicFeatureConfigurator;
 import org.glassfish.jersey.internal.Errors;
 import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.glassfish.jersey.internal.FeatureConfigurator;
 import org.glassfish.jersey.internal.JaxrsProviders;
 import org.glassfish.jersey.internal.Version;
 import org.glassfish.jersey.internal.inject.Binder;
@@ -285,7 +287,9 @@
                 new ResourceMethodInvokerConfigurator(),
                 new ProcessingProvidersConfigurator(),
                 new ContainerProviderConfigurator(RuntimeType.SERVER),
-                new AutoDiscoverableConfigurator(RuntimeType.SERVER));
+                new AutoDiscoverableConfigurator(RuntimeType.SERVER),
+                new DynamicFeatureConfigurator(),
+                new FeatureConfigurator(RuntimeType.SERVER));
 
         bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
 
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/JaxRsFeatureRegistrationTest.java b/core-server/src/test/java/org/glassfish/jersey/server/JaxRsFeatureRegistrationTest.java
new file mode 100644
index 0000000..122ab45
--- /dev/null
+++ b/core-server/src/test/java/org/glassfish/jersey/server/JaxRsFeatureRegistrationTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.server;
+
+import org.glassfish.jersey.CommonProperties;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+
+public class JaxRsFeatureRegistrationTest {
+
+    public static class FeatureImpl implements Feature {
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            return true;
+        }
+    }
+
+    public static class DynamicFeatureImpl implements DynamicFeature {
+
+        @Override
+        public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+        }
+    }
+
+    @Test
+    public void featureRegistrationTest() {
+        final ResourceConfig config = new ResourceConfig();
+        final ApplicationHandler ah = new ApplicationHandler(config);
+
+        Assert.assertTrue(ah.getConfiguration().isRegistered(FeatureImpl.class));
+        Assert.assertTrue(ah.getConfiguration().isRegistered(DynamicFeatureImpl.class));
+    }
+
+    @Test
+    public void serviceLoadingPropertyTest() {
+
+        final ResourceConfig config = new ResourceConfig()
+                .property(CommonProperties.JAXRS_SERVICE_LOADING_ENABLE, "false");
+        final ApplicationHandler ah = new ApplicationHandler(config);
+
+        Assert.assertFalse(ah.getConfiguration().isRegistered(FeatureImpl.class));
+        Assert.assertFalse(ah.getConfiguration().isRegistered(DynamicFeatureImpl.class));
+    }
+}
\ No newline at end of file
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java b/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java
index f982a16..7d4626f 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates.
  *
  * This program and the accompanying materials are made available under the
@@ -25,7 +25,9 @@
 import org.glassfish.jersey.internal.AutoDiscoverableConfigurator;
 import org.glassfish.jersey.internal.BootstrapConfigurator;
 import org.glassfish.jersey.internal.ContextResolverFactory;
+import org.glassfish.jersey.internal.DynamicFeatureConfigurator;
 import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.glassfish.jersey.internal.FeatureConfigurator;
 import org.glassfish.jersey.internal.inject.AbstractBinder;
 import org.glassfish.jersey.internal.inject.Binder;
 import org.glassfish.jersey.internal.inject.InjectionManager;
@@ -113,7 +115,9 @@
                 new ExternalRequestScopeConfigurator(),
                 new ModelProcessorConfigurator(),
                 new ResourceModelConfigurator(),
-                new ServerExecutorProvidersConfigurator());
+                new ServerExecutorProvidersConfigurator(),
+                new DynamicFeatureConfigurator(),
+                new FeatureConfigurator(RuntimeType.SERVER));
 
         bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
 
diff --git a/core-server/src/test/resources/META-INF/services/javax.ws.rs.container.DynamicFeature b/core-server/src/test/resources/META-INF/services/javax.ws.rs.container.DynamicFeature
new file mode 100644
index 0000000..56c113e
--- /dev/null
+++ b/core-server/src/test/resources/META-INF/services/javax.ws.rs.container.DynamicFeature
@@ -0,0 +1 @@
+org.glassfish.jersey.server.JaxRsFeatureRegistrationTest$DynamicFeatureImpl
\ No newline at end of file
diff --git a/core-server/src/test/resources/META-INF/services/javax.ws.rs.core.Feature b/core-server/src/test/resources/META-INF/services/javax.ws.rs.core.Feature
new file mode 100644
index 0000000..050876b
--- /dev/null
+++ b/core-server/src/test/resources/META-INF/services/javax.ws.rs.core.Feature
@@ -0,0 +1 @@
+org.glassfish.jersey.server.JaxRsFeatureRegistrationTest$FeatureImpl
\ No newline at end of file