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 8001bd5d1c..b6adbcc8e9 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -58,6 +58,16 @@ public final class CommonProperties {
"jersey.config.disableMoxyJson.server");
}
+ /**
+ * Property which allows (if true) default System properties configuration provider.
+ *
+ * If an external properties provider is used, the system properties are not used.
+ *
+ * Shall be set to turn on the ability to propagate system properties to Jersey configuration.
+ * @since 2.29
+ */
+ public static final String ALLOW_SYSTEM_PROPERTIES_PROVIDER = "jersey.config.allowSystemPropertiesProvider";
+
/**
* If {@code true} then disable feature auto discovery globally on client/server.
*
@@ -217,15 +227,21 @@ public final class CommonProperties {
public static final String OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER = "jersey.config.server.contentLength.buffer";
/**
- * Property which allows (if true) default System properties configuration provider.
- *
- * Effective if there are no any external properties providers
+ * Disable some of the default providers from being loaded. The following providers extend application footprint
+ * by XML dependencies, which is too heavy for native image, or by AWT which may possibly be not available by JDK 11 desktop:
+ *
+ * - java.awt.image.RenderedImage
+ * - javax.xml.transform.Source
+ * - javax.xml.transform.dom.DOMSource
+ * - javax.xml.transform.sax.SAXSource
+ * - javax.xml.transform.stream.StreamSource
+ *
+ * The following are the options to disable the provides: {@code DOMSOURCE, RENDEREDIMAGE, SAXSOURCE, SOURCE, STREAMSOURCE},
+ * or to disable all: {@code ALL}. Multiple options can be disabled by adding multiple comma separated values.
*
- * Shall be set (if used) in system properties.
- * @since 2.29
+ * @since 2.30
*/
-
- public static final String ALLOW_SYSTEM_PROPERTIES_PROVIDER = "jersey.config.allowSystemPropertiesProvider";
+ public static final String PROVIDER_DEFAULT_DISABLE = "jersey.config.disableDefaultProvider";
/**
* Prevent instantiation.
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
index 95b000d2fb..d551327a2c 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
@@ -16,7 +16,10 @@
package org.glassfish.jersey.message.internal;
+import java.security.AccessController;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -26,8 +29,11 @@
import javax.inject.Singleton;
+import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.ServiceFinderBinder;
import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.internal.util.Tokenizer;
import org.glassfish.jersey.spi.HeaderDelegateProvider;
/**
@@ -77,20 +83,28 @@ protected void configure() {
bindSingletonWorker(InputStreamProvider.class);
bindSingletonWorker(BasicTypesMessageProvider.class);
bindSingletonWorker(ReaderProvider.class);
- bindSingletonWorker(RenderedImageProvider.class);
+ // bindSingletonWorker(RenderedImageProvider.class); - enabledProvidersBinder
bindSingletonWorker(StringMessageProvider.class);
- // Message body readers
- bind(SourceProvider.StreamSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
- bind(SourceProvider.SaxSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
- bind(SourceProvider.DomSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ // Message body readers -- enabledProvidersBinder
+ // bind(SourceProvider.StreamSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ // bind(SourceProvider.SaxSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ // bind(SourceProvider.DomSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
/*
* TODO: com.sun.jersey.core.impl.provider.entity.EntityHolderReader
*/
// Message body writers
bind(StreamingOutputProvider.class).to(MessageBodyWriter.class).in(Singleton.class);
- bind(SourceProvider.SourceWriter.class).to(MessageBodyWriter.class).in(Singleton.class);
+ // bind(SourceProvider.SourceWriter.class).to(MessageBodyWriter.class).in(Singleton.class); - enabledProvidersBinder
+
+ final EnabledProvidersBinder enabledProvidersBinder = new EnabledProvidersBinder();
+ if (applicationProperties != null && applicationProperties.get(CommonProperties.PROVIDER_DEFAULT_DISABLE) != null) {
+ enabledProvidersBinder.markDisabled(
+ String.valueOf(applicationProperties.get(CommonProperties.PROVIDER_DEFAULT_DISABLE))
+ );
+ }
+ enabledProvidersBinder.bindToBinder(this);
// Header Delegate Providers registered in META-INF.services
install(new ServiceFinderBinder<>(HeaderDelegateProvider.class, applicationProperties, runtimeType));
@@ -137,4 +151,115 @@ public Set getHeaderDelegateProviders() {
return providers;
}
}
+
+ private static final class EnabledProvidersBinder {
+ private enum Provider {
+ DOMSOURCE ("javax.xml.transform.dom.DOMSource"),
+ RENDEREDIMAGE ("java.awt.image.RenderedImage"),
+ SAXSOURCE ("javax.xml.transform.sax.SAXSource"),
+ SOURCE ("javax.xml.transform.Source"),
+ STREAMSOURCE ("javax.xml.transform.stream.StreamSource");
+ Provider(String className) {
+ this.className = className;
+ }
+ private String className;
+ }
+
+ private static final String ALL = "ALL";
+ private HashSet enabledProviders = new HashSet<>();
+
+ private EnabledProvidersBinder() {
+ for (Provider provider : Provider.values()) {
+ enabledProviders.add(provider);
+ }
+ }
+
+ private void markDisabled(String properties) {
+ String[] tokens = Tokenizer.tokenize(properties);
+ for (int tokenIndex = 0; tokenIndex != tokens.length; tokenIndex++) {
+ String token = tokens[tokenIndex].toUpperCase(Locale.ROOT);
+ if (ALL.equals(token)) {
+ enabledProviders.clear();
+ return;
+ }
+ for (Iterator iterator = enabledProviders.iterator(); iterator.hasNext();) {
+ Provider provider = iterator.next();
+ if (provider.name().equals(token)) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ private void bindToBinder(AbstractBinder binder) {
+ ProviderBinder providerBinder = null;
+ for (Provider provider : enabledProviders) {
+ if (isClass(provider.className)) {
+ switch (provider) {
+ case DOMSOURCE:
+ providerBinder = new DomSourceBinder();
+ break;
+ case RENDEREDIMAGE:
+ providerBinder = new RenderedImageBinder();
+ break;
+ case SAXSOURCE:
+ providerBinder = new SaxSourceBinder();
+ break;
+ case SOURCE:
+ providerBinder = new SourceBinder();
+ break;
+ case STREAMSOURCE:
+ providerBinder = new StreamSourceBinder();
+ break;
+ }
+ providerBinder.bind(binder, provider);
+ }
+ }
+ }
+
+ private static boolean isClass(String className) {
+ return null != AccessController.doPrivileged(ReflectionHelper.classForNamePA(className));
+ }
+
+
+ private interface ProviderBinder {
+ void bind(AbstractBinder binder, Provider provider);
+ }
+
+ private class DomSourceBinder implements ProviderBinder {
+ @Override
+ public void bind(AbstractBinder binder, Provider provider) {
+ binder.bind(SourceProvider.DomSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ }
+ }
+
+ private class RenderedImageBinder implements ProviderBinder {
+ @Override
+ public void bind(AbstractBinder binder, Provider provider) {
+ binder.bind(RenderedImageProvider.class)
+ .to(MessageBodyReader.class).to(MessageBodyWriter.class).in(Singleton.class);
+ }
+ }
+
+ private class SaxSourceBinder implements ProviderBinder {
+ @Override
+ public void bind(AbstractBinder binder, Provider provider) {
+ binder.bind(SourceProvider.SaxSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ }
+ }
+
+ private class SourceBinder implements ProviderBinder {
+ @Override
+ public void bind(AbstractBinder binder, Provider provider) {
+ binder.bind(SourceProvider.SourceWriter.class).to(MessageBodyWriter.class).in(Singleton.class);
+ }
+ }
+
+ private class StreamSourceBinder implements ProviderBinder {
+ @Override
+ public void bind(AbstractBinder binder, Provider provider) {
+ binder.bind(SourceProvider.StreamSourceReader.class).to(MessageBodyReader.class).in(Singleton.class);
+ }
+ }
+ }
}
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/config/DisabledProvidersTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/config/DisabledProvidersTest.java
new file mode 100644
index 0000000000..cccd36c564
--- /dev/null
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/config/DisabledProvidersTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 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.glassfish.jersey.internal.inject.ClassBinding;
+import org.glassfish.jersey.message.internal.MessagingBinders;
+import org.glassfish.jersey.message.internal.RenderedImageProvider;
+import org.glassfish.jersey.message.internal.SourceProvider;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.ws.rs.RuntimeType;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+public class DisabledProvidersTest {
+ private static class DisabledProvidersChecker extends MessagingBinders.MessageBodyProviders {
+ private HashSet> bindSet = new HashSet<>();
+ public DisabledProvidersChecker(Map applicationProperties, RuntimeType runtimeType) {
+ super(applicationProperties, runtimeType);
+ }
+
+ @Override
+ public ClassBinding bind(Class serviceType) {
+ bindSet.add(serviceType);
+ return super.bind(serviceType);
+ }
+
+ @Override
+ public void configure() {
+ super.configure();
+ }
+ }
+
+ @Test
+ public void testNoRenderedImageProviderNoSourceProvider() {
+ Map properties = new HashMap<>();
+ properties.put(CommonProperties.PROVIDER_DEFAULT_DISABLE, "RENDEREDIMAGE, SOURCE");
+
+ DisabledProvidersChecker checker = new DisabledProvidersChecker(properties, RuntimeType.CLIENT);
+ checker.configure();
+ Assert.assertFalse(checker.bindSet.contains(RenderedImageProvider.class));
+ Assert.assertFalse(checker.bindSet.contains(SourceProvider.SourceWriter.class));
+ Assert.assertTrue(checker.bindSet.contains(SourceProvider.StreamSourceReader.class));
+ Assert.assertTrue(checker.bindSet.contains(SourceProvider.SaxSourceReader.class));
+ Assert.assertTrue(checker.bindSet.contains(SourceProvider.DomSourceReader.class));
+ }
+
+ @Test
+ public void testNoDisabledProvider() {
+ Map properties = new HashMap<>();
+ properties.put(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");
+
+ DisabledProvidersChecker checker = new DisabledProvidersChecker(properties, RuntimeType.CLIENT);
+ checker.configure();
+ Assert.assertFalse(checker.bindSet.contains(RenderedImageProvider.class));
+ Assert.assertFalse(checker.bindSet.contains(SourceProvider.StreamSourceReader.class));
+ Assert.assertFalse(checker.bindSet.contains(SourceProvider.SourceWriter.class));
+ Assert.assertFalse(checker.bindSet.contains(SourceProvider.SaxSourceReader.class));
+ Assert.assertFalse(checker.bindSet.contains(SourceProvider.DomSourceReader.class));
+ }
+
+
+}