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