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: + *

+ * 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)); + } + + +}