Skip to content

Commit

Permalink
Allow to disable certain default providers (#4342)
Browse files Browse the repository at this point in the history
* Allow to disable certain default providers

Signed-off-by: Jan Supol <[email protected]>
  • Loading branch information
jansupol authored Dec 18, 2019
1 parent a5c09a8 commit af5d00a
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
Expand Down Expand Up @@ -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:
* <ul>
* <li>java.awt.image.RenderedImage</li>
* <li>javax.xml.transform.Source</li>
* <li>javax.xml.transform.dom.DOMSource</li>
* <li>javax.xml.transform.sax.SAXSource</li>
* <li>javax.xml.transform.stream.StreamSource</li>
* </ul>
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;

/**
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -137,4 +151,115 @@ public Set<HeaderDelegateProvider> 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<Provider> 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<Provider> 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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Class<?>> bindSet = new HashSet<>();
public DisabledProvidersChecker(Map<String, Object> applicationProperties, RuntimeType runtimeType) {
super(applicationProperties, runtimeType);
}

@Override
public <T> ClassBinding<T> bind(Class<T> serviceType) {
bindSet.add(serviceType);
return super.bind(serviceType);
}

@Override
public void configure() {
super.configure();
}
}

@Test
public void testNoRenderedImageProviderNoSourceProvider() {
Map<String, Object> 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<String, Object> 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));
}


}

0 comments on commit af5d00a

Please sign in to comment.