Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to disable certain default providers #4342

Merged
merged 3 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}


}