Allow for passing in additional property files to configure additional configs

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalConfigurationProviderImpl.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalConfigurationProviderImpl.java
new file mode 100644
index 0000000..8377e63
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalConfigurationProviderImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 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.config;
+
+import org.glassfish.jersey.spi.ExternalConfigurationModel;
+import org.glassfish.jersey.spi.ExternalConfigurationProvider;
+
+import java.util.Map;
+
+public class ExternalConfigurationProviderImpl implements ExternalConfigurationProvider {
+
+    protected final ExternalConfigurationModel<?> model;
+
+    protected ExternalConfigurationProviderImpl(ExternalConfigurationModel<?> model) {
+        this.model = model;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return model.getProperties();
+    }
+
+    @Override
+    public ExternalConfigurationModel getConfiguration() {
+        return model;
+    }
+
+    @Override
+    public ExternalConfigurationModel merge(ExternalConfigurationModel input) {
+        return input == null ? model : model.mergeProperties(input.getProperties());
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactory.java
index 35ca77a..b270751 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactory.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -32,6 +32,7 @@
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.function.BiConsumer;
 
 /**
  * Factory for external properties providers
@@ -50,11 +51,19 @@
      * @return map of merged properties from all found/plugged providers
      */
     static Map<String, Object> readExternalPropertiesMap() {
-
-        final ExternalConfigurationProvider provider = mergeConfigs(EXTERNAL_CONFIGURATION_PROVIDERS);
-        return provider == null ? Collections.emptyMap() : provider.getProperties();
+        return readExternalPropertiesMap(EXTERNAL_CONFIGURATION_PROVIDERS);
     }
 
+    /**
+     * Map of merged properties from all given providers
+     *
+     * @param externalConfigProviders list of providers to use
+     * @return map of merged properties from {@code externalConfigProviders} providers
+     */
+    private static Map<String, Object> readExternalPropertiesMap(List<ExternalConfigurationProvider> externalConfigProviders) {
+        final ExternalConfigurationProvider provider = mergeConfigs(externalConfigProviders);
+        return provider == null ? Collections.emptyMap() : provider.getProperties();
+    }
 
     /**
      * Input Configurable object shall be provided in order to be filled with all found properties
@@ -64,14 +73,28 @@
      */
 
     public static boolean configure(Configurable config) {
+        return configure((k, v) -> config.property(k, v), EXTERNAL_CONFIGURATION_PROVIDERS);
+    }
 
+    /**
+     * Key Value pairs gathered by {@link ExternalConfigurationProvider}s are applied to a given {@code config}. The
+     * {@code config} can be for instance {@code (k,v) -> configurable.property(k,v)} of a
+     * {@link Configurable#property(String, Object) Configurable structure}, or {@code (k,v) -> properties.put(k,v)} of a
+     * {@link java.util.Properties#put(Object, Object) Properties structure}.
+     *
+     * @param config
+     * @param externalConfigurationProviders the providers to grab the properties from it.
+     * @return true if configured false otherwise.
+     */
+    public static boolean configure(BiConsumer<String, Object> config,
+                                    List<ExternalConfigurationProvider> externalConfigurationProviders) {
         if (config instanceof ExternalConfigurationModel) {
             return false; //shall not configure itself
         }
 
-        final Map<String, Object> properties = readExternalPropertiesMap();
+        final Map<String, Object> properties = readExternalPropertiesMap(externalConfigurationProviders);
 
-        properties.forEach((k, v) -> config.property(k, v));
+        properties.forEach((k, v) -> config.accept(k, v));
 
         return true;
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
new file mode 100644
index 0000000..4d9606b
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, 2022 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.config;
+
+import java.util.Arrays;
+import java.util.List;
+
+class JerseySystemPropertiesConfigurationModel extends SystemPropertiesConfigurationModel {
+
+    static final List<String> PROPERTY_CLASSES = Arrays.asList(
+            "org.glassfish.jersey.CommonProperties",
+            "org.glassfish.jersey.ExternalProperties",
+            "org.glassfish.jersey.server.ServerProperties",
+            "org.glassfish.jersey.client.ClientProperties",
+            "org.glassfish.jersey.servlet.ServletProperties",
+            "org.glassfish.jersey.message.MessageProperties",
+            "org.glassfish.jersey.apache.connector.ApacheClientProperties",
+            "org.glassfish.jersey.helidon.connector.HelidonClientProperties",
+            "org.glassfish.jersey.jdk.connector.JdkConnectorProperties",
+            "org.glassfish.jersey.jetty.connector.JettyClientProperties",
+            "org.glassfish.jersey.netty.connector.NettyClientProperties",
+            "org.glassfish.jersey.media.multipart.MultiPartProperties",
+            "org.glassfish.jersey.server.oauth1.OAuth1ServerProperties");
+
+    JerseySystemPropertiesConfigurationModel() {
+        super(PROPERTY_CLASSES);
+    }
+}
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java
index f870fab..ee08cea 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -27,6 +27,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.logging.Logger;
 
@@ -39,36 +40,39 @@
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.spi.ExternalConfigurationModel;
 
+/**
+ * The External Configuration Model that supports {@code System} properties. The properties are listed in a property class
+ * in a form of {@code public static final String} property name. The {@code String} value of the property name is searched
+ * among the {@code System} properties. The property scan is performed only when
+ * {@link CommonProperties#ALLOW_SYSTEM_PROPERTIES_PROVIDER} is set to {@code true}.
+ */
+public class SystemPropertiesConfigurationModel implements ExternalConfigurationModel<Void> {
 
-class SystemPropertiesConfigurationModel implements ExternalConfigurationModel<Void> {
-
-    private static final Logger log = Logger.getLogger(SystemPropertiesConfigurationModel.class.getName());
-    static final List<String> PROPERTY_CLASSES = Arrays.asList(
-            "org.glassfish.jersey.ExternalProperties",
-            "org.glassfish.jersey.server.ServerProperties",
-            "org.glassfish.jersey.client.ClientProperties",
-            "org.glassfish.jersey.servlet.ServletProperties",
-            "org.glassfish.jersey.message.MessageProperties",
-            "org.glassfish.jersey.apache.connector.ApacheClientProperties",
-            "org.glassfish.jersey.helidon.connector.HelidonClientProperties",
-            "org.glassfish.jersey.jdk.connector.JdkConnectorProperties",
-            "org.glassfish.jersey.jetty.connector.JettyClientProperties",
-            "org.glassfish.jersey.netty.connector.NettyClientProperties",
-            "org.glassfish.jersey.media.multipart.MultiPartProperties",
-            "org.glassfish.jersey.server.oauth1.OAuth1ServerProperties");
-
+    private static final Logger LOGGER = Logger.getLogger(SystemPropertiesConfigurationModel.class.getName());
 
     private static final Map<Class, Function> converters = new HashMap<>();
+    private final Map<String, Object> properties = new HashMap<>();
+    private final AtomicBoolean gotProperties = new AtomicBoolean(false);
+    private final List<String> propertyClassNames;
     static {
         converters.put(String.class, (Function<String, String>) s -> s);
         converters.put(Integer.class, (Function<String, Integer>) s -> Integer.valueOf(s));
+        converters.put(Long.class, (Function<String, Long>) s -> Long.parseLong(s));
         converters.put(Boolean.class, (Function<String, Boolean>) s -> s.equalsIgnoreCase("1")
                 ? true
                 : Boolean.parseBoolean(s));
     }
 
-    private String getSystemProperty(String name) {
-        return AccessController.doPrivileged(PropertiesHelper.getSystemProperty(name));
+    /**
+     * Create new {@link ExternalConfigurationModel} for properties defined by classes in {@code propertyClassNames} list.
+     * @param propertyClassNames List of property defining class names.
+     */
+    public SystemPropertiesConfigurationModel(List<String> propertyClassNames) {
+        this.propertyClassNames = propertyClassNames;
+    }
+
+    protected List<String> getPropertyClassNames() {
+        return propertyClassNames;
     }
 
     @Override
@@ -76,13 +80,10 @@
         if (converters.get(clazz) == null) {
             throw new IllegalArgumentException("Unsupported class type");
         }
-        return (name != null && clazz != null && isProperty(name))
+        return (name != null && clazz != null && hasProperty(name))
                 ? clazz.cast(converters.get(clazz).apply(getSystemProperty(name)))
                 : null;
     }
-
-
-
     @Override
     public <T> Optional<T> getOptionalProperty(String name, Class<T> clazz) {
         return Optional.of(as(name, clazz));
@@ -90,6 +91,7 @@
 
     @Override
     public ExternalConfigurationModel mergeProperties(Map<String, Object> inputProperties) {
+        inputProperties.forEach((k, v) -> properties.put(k, v));
         return this;
     }
 
@@ -100,11 +102,11 @@
 
     @Override
     public boolean isProperty(String name) {
-        return Optional.ofNullable(
-                AccessController.doPrivileged(
-                        PropertiesHelper.getSystemProperty(name)
-                )
-        ).isPresent();
+        String property = getSystemProperty(name);
+        return property != null && (
+                "0".equals(property) || "1".equals(property)
+                        || "true".equalsIgnoreCase(property) || "false".equalsIgnoreCase(property)
+        );
     }
 
     @Override
@@ -114,30 +116,29 @@
 
     @Override
     public Map<String, Object> getProperties() {
-        final Map<String, Object> result = new HashMap<>();
-
         final Boolean allowSystemPropertiesProvider = as(
                 CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.class
         );
         if (!Boolean.TRUE.equals(allowSystemPropertiesProvider)) {
-            log.finer(LocalizationMessages.WARNING_PROPERTIES());
-            return result;
+            LOGGER.finer(LocalizationMessages.WARNING_PROPERTIES());
+            return properties;
         }
 
-        try {
-            AccessController.doPrivileged(PropertiesHelper.getSystemProperties())
-                    .forEach((k, v) -> result.put(String.valueOf(k), v));
-        } catch (SecurityException se) {
-            log.warning(LocalizationMessages.SYSTEM_PROPERTIES_WARNING());
-            return getExpectedSystemProperties();
+        if (gotProperties.compareAndSet(false, true)) {
+            try {
+                AccessController.doPrivileged(PropertiesHelper.getSystemProperties())
+                        .forEach((k, v) -> properties.put(String.valueOf(k), v));
+            } catch (SecurityException se) {
+                LOGGER.warning(LocalizationMessages.SYSTEM_PROPERTIES_WARNING());
+                return getExpectedSystemProperties();
+            }
         }
-        return result;
+        return properties;
     }
 
     private Map<String, Object> getExpectedSystemProperties() {
         final Map<String, Object> result = new HashMap<>();
-        mapFieldsToProperties(result, CommonProperties.class);
-        for (String propertyClass : PROPERTY_CLASSES) {
+        for (String propertyClass : getPropertyClassNames()) {
             mapFieldsToProperties(result,
                     AccessController.doPrivileged(
                             ReflectionHelper.classForNamePA(propertyClass)
@@ -148,7 +149,7 @@
         return  result;
     }
 
-    private <T> void mapFieldsToProperties(Map<String, Object> properties, Class<T> clazz) {
+    private static <T> void mapFieldsToProperties(Map<String, Object> properties, Class<T> clazz) {
         if (clazz == null) {
             return;
         }
@@ -170,17 +171,21 @@
         }
     }
 
-    private String getPropertyNameByField(Field field) {
+    private static String getPropertyNameByField(Field field) {
         return  AccessController.doPrivileged((PrivilegedAction<String>) () -> {
             try {
                 return (String) field.get(null);
             } catch (IllegalAccessException e) {
-                log.warning(e.getLocalizedMessage());
+                LOGGER.warning(e.getLocalizedMessage());
             }
             return null;
         });
     }
 
+    private static String getSystemProperty(String name) {
+        return AccessController.doPrivileged(PropertiesHelper.getSystemProperty(name));
+    }
+
     @Override
     public Object getProperty(String name) {
         return getSystemProperty(name);
@@ -188,7 +193,7 @@
 
     @Override
     public Collection<String> getPropertyNames() {
-        return PropertiesHelper.getSystemProperties().run().stringPropertyNames();
+        return AccessController.doPrivileged(PropertiesHelper.getSystemProperties()).stringPropertyNames();
     }
 
     @Override
@@ -225,4 +230,9 @@
     public Set<Object> getInstances() {
         return null;
     }
+
+    // Jersey 2.x
+    private boolean hasProperty(String name) {
+        return getProperty(name) != null;
+    }
 }
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationProvider.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationProvider.java
index 145f5f7..2ba0f2f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -16,27 +16,9 @@
 
 package org.glassfish.jersey.internal.config;
 
-import org.glassfish.jersey.spi.ExternalConfigurationModel;
-import org.glassfish.jersey.spi.ExternalConfigurationProvider;
+class SystemPropertiesConfigurationProvider extends ExternalConfigurationProviderImpl {
 
-import java.util.Map;
-
-class SystemPropertiesConfigurationProvider implements ExternalConfigurationProvider {
-
-    private final SystemPropertiesConfigurationModel model = new SystemPropertiesConfigurationModel();
-    @Override
-    public Map<String, Object> getProperties() {
-        return model.getProperties();
+    public SystemPropertiesConfigurationProvider() {
+        super(new JerseySystemPropertiesConfigurationModel());
     }
-
-    @Override
-    public ExternalConfigurationModel getConfiguration() {
-        return model;
-    }
-
-    @Override
-    public ExternalConfigurationModel merge(ExternalConfigurationModel input) {
-        return input == null ? model : model.mergeProperties(input.getProperties());
-    }
-
 }
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/config/AdditionalSystemPropertiesTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/config/AdditionalSystemPropertiesTest.java
new file mode 100644
index 0000000..1cc853e
--- /dev/null
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/config/AdditionalSystemPropertiesTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 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.config;
+
+import org.glassfish.jersey.CommonProperties;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Properties;
+
+public class AdditionalSystemPropertiesTest {
+
+    private static class AdditionalSystemProperties {
+        public static final String FIRST_PROPERTY = "first.property";
+        public static final String SECOND_PROPERTY = "second.property";
+    }
+
+    @BeforeClass
+    public static void setUp() {
+        System.setProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.TRUE.toString());
+        System.getProperties().put(AdditionalSystemProperties.FIRST_PROPERTY, "first value");
+        System.getProperties().put(AdditionalSystemProperties.SECOND_PROPERTY, "second value");
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        System.clearProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER);
+        System.clearProperty(AdditionalSystemProperties.FIRST_PROPERTY);
+        System.clearProperty(AdditionalSystemProperties.SECOND_PROPERTY);
+    }
+
+    @Test
+    public void readAdditionalSystemPropertiesTest() {
+        SystemPropertiesConfigurationModel testModel = new SystemPropertiesConfigurationModel(
+                Collections.singletonList(AdditionalSystemProperties.class.getName())
+        );
+
+        Properties properties = new Properties();
+        ExternalPropertiesConfigurationFactory.configure((k, v) -> properties.put(k, v),
+                Collections.singletonList(new ExternalConfigurationProviderImpl(testModel))
+        );
+
+        Assert.assertFalse(properties.isEmpty());
+        Assert.assertTrue(properties.containsKey(AdditionalSystemProperties.FIRST_PROPERTY));
+        Assert.assertTrue(properties.containsKey(AdditionalSystemProperties.SECOND_PROPERTY));
+        Assert.assertEquals("first value", properties.get(AdditionalSystemProperties.FIRST_PROPERTY));
+        Assert.assertEquals("second value", properties.get(AdditionalSystemProperties.SECOND_PROPERTY));
+    }
+}
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
index d0df568..f6e6758 100644
--- a/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -55,9 +55,13 @@
                 readExternalPropertiesMap().get("jersey.config.server.provider.scanning.recursive");
         Assert.assertNull(result);
         Assert.assertEquals(Boolean.TRUE,
+                getConfig().isProperty(CommonProperties.JSON_PROCESSING_FEATURE_DISABLE));
+        Assert.assertEquals(Boolean.TRUE,
                 getConfig().as(CommonProperties.JSON_PROCESSING_FEATURE_DISABLE, Boolean.class));
         Assert.assertEquals(Boolean.FALSE,
                 getConfig().as("jersey.config.client.readTimeout", Boolean.class));
+        Assert.assertEquals(Boolean.FALSE,
+                getConfig().isProperty("jersey.config.client.readTimeout"));
         Assert.assertEquals(1,
                 getConfig().as(CommonProperties.JSON_PROCESSING_FEATURE_DISABLE, Integer.class));
         Assert.assertEquals(10,
diff --git a/ext/microprofile/mp-config/src/main/java/org/glassfish/jersey/microprofile/config/ConfigurationProvider.java b/ext/microprofile/mp-config/src/main/java/org/glassfish/jersey/microprofile/config/ConfigurationProvider.java
index df50eee..693c424 100644
--- a/ext/microprofile/mp-config/src/main/java/org/glassfish/jersey/microprofile/config/ConfigurationProvider.java
+++ b/ext/microprofile/mp-config/src/main/java/org/glassfish/jersey/microprofile/config/ConfigurationProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -16,35 +16,12 @@
 
 package org.glassfish.jersey.microprofile.config;
 
-import org.eclipse.microprofile.config.Config;
 import org.eclipse.microprofile.config.ConfigProvider;
-import org.glassfish.jersey.spi.ExternalConfigurationModel;
-import org.glassfish.jersey.spi.ExternalConfigurationProvider;
+import org.glassfish.jersey.internal.config.ExternalConfigurationProviderImpl;
 
-import java.util.Map;
-
-public class ConfigurationProvider implements ExternalConfigurationProvider {
-
-
-    private ConfigurationModel<Config> configModel;
+public class ConfigurationProvider extends ExternalConfigurationProviderImpl {
 
     public ConfigurationProvider() {
-
-        configModel = new ConfigurationModel(ConfigProvider.getConfig());
-    }
-
-    @Override
-    public Map<String, Object> getProperties() {
-        return configModel.getProperties();
-    }
-
-    @Override
-    public ExternalConfigurationModel getConfiguration() {
-        return configModel;
-    }
-
-    @Override
-    public ExternalConfigurationModel merge(ExternalConfigurationModel input) {
-        return input == null ? this.configModel : this.configModel.mergeProperties(input.getProperties());
+        super(new ConfigurationModel(ConfigProvider.getConfig()));
     }
 }
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
index 1e548e5..f943686 100644
--- a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022 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
@@ -61,7 +61,7 @@
          *  (jdk.internal.loader.ClassLoaders$AppClassLoader) that current Guava version doesn't support.
          */
         if (JdkVersion.getJdkVersion().getMajor() == 8) {
-            SystemPropertiesConfigurationModel model = new SystemPropertiesConfigurationModel();
+            JerseySystemPropertiesConfigurationModel model = new JerseySystemPropertiesConfigurationModel();
             Predicate<Class<?>> containsAnnotation = clazz -> clazz.getAnnotation(PropertiesClass.class) != null
                     || clazz.getAnnotation(Property.class) != null;
             Predicate<Class<?>> notCommon = clazz -> clazz != CommonProperties.class;
@@ -69,7 +69,7 @@
             List<String> propertyClasses = getClassesWithPredicate(ROOT_PACKAGE, notCommon, notTestProperties,
                     containsAnnotation).stream().map(Class::getName).collect(Collectors.toList());
             assertFalse(propertyClasses.isEmpty());
-            propertyClasses.removeAll(SystemPropertiesConfigurationModel.PROPERTY_CLASSES);
+            propertyClasses.removeAll(JerseySystemPropertiesConfigurationModel.PROPERTY_CLASSES);
             assertEquals("New properties have been found. "
                     + "Make sure you add next classes in SystemPropertiesConfigurationModel.PROPERTY_CLASSES: "
                     + propertyClasses, 0, propertyClasses.size());
@@ -91,7 +91,7 @@
             System.setProperty(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION, TEST_STRING);
             System.setProperty(MultiPartProperties.TEMP_DIRECTORY, TEST_STRING);
             System.setProperty(OAuth1ServerProperties.REALM, TEST_STRING);
-            SystemPropertiesConfigurationModel model = new SystemPropertiesConfigurationModel();
+            JerseySystemPropertiesConfigurationModel model = new JerseySystemPropertiesConfigurationModel();
             assertTrue(model.as(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.class));
             String securityPolicy = SystemPropertiesConfigurationModelTest.class.getResource("/server.policy").getFile();
             System.setProperty("java.security.policy", securityPolicy);