Parametrize ParamConverters to allow throwing IAE (#5349)
* Parametrize ParamConverters to allow throwing IAE
Signed-off-by: jansupol <jan.supol@oracle.com>
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 762a08b..bc76ba6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -305,6 +305,22 @@
public static final String JSON_JACKSON_DISABLED_MODULES_SERVER = "jersey.config.server.json.jackson.disabled.modules";
/**
+ * <p>
+ * Force the {@link javax.ws.rs.ext.ParamConverter} to throw {@link IllegalArgumentException} as mandated in javadoc.
+ * Must be convertible to {@link Boolean} value.
+ * </p>
+ * <p>
+ * Internally the {@code Exception} is caught by Jersey and usually converted to {@code null}.
+ * Therefore, the default value is set to {@code false} to speed-up the conversion.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * @since 2.40
+ */
+ public static final String PARAM_CONVERTERS_THROW_IAE = "jersey.config.paramconverters.throw.iae";
+
+ /**
* Prevent instantiation.
*/
private CommonProperties() {
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
index 699a2b7..4a1ca86 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
@@ -35,10 +35,12 @@
import javax.inject.Singleton;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
+import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.ClassTypePair;
@@ -55,12 +57,32 @@
@Singleton
public class ParamConverters {
- private abstract static class AbstractStringReader<T> implements ParamConverter<T> {
+ private static class ParamConverterCompliance {
+ protected final boolean canReturnNull;
+
+ private ParamConverterCompliance(boolean canReturnNull) {
+ this.canReturnNull = canReturnNull;
+ }
+
+ protected <T> T nullOrThrow() {
+ if (canReturnNull) {
+ return null;
+ } else {
+ throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+ }
+ }
+ }
+
+ private abstract static class AbstractStringReader<T> extends ParamConverterCompliance implements ParamConverter<T> {
+
+ private AbstractStringReader(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public T fromString(final String value) {
if (value == null) {
- return null;
+ return nullOrThrow();
}
try {
return _fromString(value);
@@ -85,7 +107,7 @@
@Override
public String toString(final T value) throws IllegalArgumentException {
if (value == null) {
- return null;
+ return nullOrThrow();
}
return value.toString();
}
@@ -97,7 +119,11 @@
* by invoking a single {@code String} parameter constructor on the target type.
*/
@Singleton
- public static class StringConstructor implements ParamConverterProvider {
+ public static class StringConstructor extends ParamConverterCompliance implements ParamConverterProvider {
+
+ private StringConstructor(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -106,7 +132,7 @@
final Constructor constructor = AccessController.doPrivileged(ReflectionHelper.getStringConstructorPA(rawType));
- return (constructor == null) ? null : new AbstractStringReader<T>() {
+ return (constructor == null) ? null : new AbstractStringReader<T>(canReturnNull) {
@Override
protected T _fromString(final String value) throws Exception {
@@ -122,7 +148,11 @@
* by invoking a static {@code valueOf(String)} method on the target type.
*/
@Singleton
- public static class TypeValueOf implements ParamConverterProvider {
+ public static class TypeValueOf extends ParamConverterCompliance implements ParamConverterProvider {
+
+ private TypeValueOf(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -131,7 +161,7 @@
final Method valueOf = AccessController.doPrivileged(ReflectionHelper.getValueOfStringMethodPA(rawType));
- return (valueOf == null) ? null : new AbstractStringReader<T>() {
+ return (valueOf == null) ? null : new AbstractStringReader<T>(canReturnNull) {
@Override
public T _fromString(final String value) throws Exception {
@@ -146,7 +176,11 @@
* by invoking a static {@code fromString(String)} method on the target type.
*/
@Singleton
- public static class TypeFromString implements ParamConverterProvider {
+ public static class TypeFromString extends ParamConverterCompliance implements ParamConverterProvider {
+
+ private TypeFromString(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -155,7 +189,7 @@
final Method fromStringMethod = AccessController.doPrivileged(ReflectionHelper.getFromStringStringMethodPA(rawType));
- return (fromStringMethod == null) ? null : new AbstractStringReader<T>() {
+ return (fromStringMethod == null) ? null : new AbstractStringReader<T>(canReturnNull) {
@Override
public T _fromString(final String value) throws Exception {
@@ -172,6 +206,10 @@
@Singleton
public static class TypeFromStringEnum extends TypeFromString {
+ private TypeFromStringEnum(boolean canReturnNull) {
+ super(canReturnNull);
+ }
+
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
final Type genericType,
@@ -181,7 +219,11 @@
}
@Singleton
- public static class CharacterProvider implements ParamConverterProvider {
+ public static class CharacterProvider extends ParamConverterCompliance implements ParamConverterProvider {
+
+ private CharacterProvider(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -192,7 +234,7 @@
@Override
public T fromString(String value) {
if (value == null || value.isEmpty()) {
- return null;
+ return CharacterProvider.this.nullOrThrow();
}
if (value.length() == 1) {
@@ -205,7 +247,7 @@
@Override
public String toString(T value) {
if (value == null) {
- return null;
+ return CharacterProvider.this.nullOrThrow();
}
return value.toString();
}
@@ -222,7 +264,11 @@
* {@link HttpDateFormat http date formatter} utility class.
*/
@Singleton
- public static class DateProvider implements ParamConverterProvider {
+ public static class DateProvider extends ParamConverterCompliance implements ParamConverterProvider {
+
+ private DateProvider(boolean canReturnNull) {
+ super(canReturnNull);
+ }
@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -233,7 +279,7 @@
@Override
public T fromString(final String value) {
if (value == null) {
- throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+ return DateProvider.this.nullOrThrow();
}
try {
return rawType.cast(HttpDateFormat.readDate(value));
@@ -245,7 +291,7 @@
@Override
public String toString(final T value) throws IllegalArgumentException {
if (value == null) {
- return null;
+ return DateProvider.this.nullOrThrow();
}
return value.toString();
}
@@ -258,12 +304,13 @@
* by invoking {@link ParamConverterProvider}.
*/
@Singleton
- public static class OptionalCustomProvider implements ParamConverterProvider {
+ public static class OptionalCustomProvider extends ParamConverterCompliance implements ParamConverterProvider {
// Delegates to this provider when the type of Optional is extracted.
private final InjectionManager manager;
- public OptionalCustomProvider(InjectionManager manager) {
+ public OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) {
+ super(canReturnNull);
this.manager = manager;
}
@@ -293,7 +340,7 @@
* In this case we don't send Optional.empty() because 'value' is not null.
* But we return null because the provider didn't find how to parse it.
*/
- return null;
+ return nullOrThrow();
}
}
@@ -408,17 +455,20 @@
* Create new aggregated {@link ParamConverterProvider param converter provider}.
*/
@Inject
- public AggregatedProvider(@Context InjectionManager manager) {
+ public AggregatedProvider(@Context InjectionManager manager, @Context Configuration configuration) {
+ boolean canThrowNull = !CommonProperties.getValue(configuration.getProperties(),
+ CommonProperties.PARAM_CONVERTERS_THROW_IAE,
+ Boolean.FALSE);
this.providers = new ParamConverterProvider[] {
// ordering is important (e.g. Date provider must be executed before String Constructor
// as Date has a deprecated String constructor
- new DateProvider(),
- new TypeFromStringEnum(),
- new TypeValueOf(),
- new CharacterProvider(),
- new TypeFromString(),
- new StringConstructor(),
- new OptionalCustomProvider(manager),
+ new DateProvider(canThrowNull),
+ new TypeFromStringEnum(canThrowNull),
+ new TypeValueOf(canThrowNull),
+ new CharacterProvider(canThrowNull),
+ new TypeFromString(canThrowNull),
+ new StringConstructor(canThrowNull),
+ new OptionalCustomProvider(manager, canThrowNull),
new OptionalProvider()
};
}
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
index 7ba24c8..6b54bd1 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Payara Foundation and/or its affiliates.
*
* This program and the accompanying materials are made available under the
@@ -35,6 +35,7 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
@@ -43,6 +44,8 @@
import org.glassfish.jersey.internal.inject.ParamConverters;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.ClassTypePair;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ContainerResponse;
import org.glassfish.jersey.server.RequestContextBuilder;
@@ -284,8 +287,9 @@
@Test
public void testDateParamConverterIsChosenForDateString() {
initiateWebApplication();
+ final Configuration configuration = new CommonConfig(null, ComponentBag.EXCLUDE_EMPTY);
final ParamConverter<Date> converter =
- new ParamConverters.AggregatedProvider(null).getConverter(Date.class, Date.class, null);
+ new ParamConverters.AggregatedProvider(null, configuration).getConverter(Date.class, Date.class, null);
assertEquals(ParamConverters.DateProvider.class, converter.getClass().getEnclosingClass(),
"Unexpected date converter provider class");
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 887b689..8f619b4 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -184,6 +184,21 @@
</entry>
</row>
<row>
+ <entry>&jersey.common.CommonProperties.PARAM_CONVERTERS_THROW_IAE;(Jersey 2.40 or later)
+ </entry>
+ <entry>
+ <literal>jersey.config.paramconverters.throw.iae</literal>
+ </entry>
+ <entry>
+ Force the &jaxrs.ext.ParamConverter; to throw <literal>IllegalArgumentException</literal> as mandated in javadoc.
+ Must be convertible to &lit.jdk6.Boolean; value.
+ </entry>
+ <entry>
+ Internally the <literal>Exception</literal> is caught by Jersey and usually converted to <literal>null</literal>
+ Therefore, the default value is set to &lit.false; to speed up the conversion.
+ </entry>
+ </row>
+ <row>
<entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME;
</entry>
<entry>
@@ -897,6 +912,38 @@
</entry>
</row>
<row>
+ <entry>&jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT;</entry>
+ <entry><literal>jersey.config.client.digestAuthUriCacheSizeLimit</literal></entry>
+ <entry>
+ <para>
+ The property defines a URI of a HTTP proxy the client connector should use.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE;</entry>
+ <entry><literal>jersey.config.client.request.expect.100.continue.processing</literal></entry>
+ <entry>
+ <para>
+ Allows for HTTP Expect:100-Continue being handled by the HttpUrlConnector (default Jersey
+ connector). <literal>Since 2.32</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE;</entry>
+ <entry><literal>jersey.config.client.request.expect.100.continue.threshold.size</literal></entry>
+ <entry>
+ <para>
+ Property for threshold size for content length after which Expect:100-Continue header would be applied
+ before the main request.
+ Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
+ the main request.
+ <literal>Since 2.32</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
<entry>&jersey.client.ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE;</entry>
<entry><literal>jersey.config.client.disableAutoDiscovery</literal></entry>
<entry>
@@ -1036,38 +1083,6 @@
</entry>
</row>
<row>
- <entry>&jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT;</entry>
- <entry><literal>jersey.config.client.digestAuthUriCacheSizeLimit</literal></entry>
- <entry>
- <para>
- The property defines a URI of a HTTP proxy the client connector should use.
- </para>
- </entry>
- </row>
- <row>
- <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE;</entry>
- <entry><literal>jersey.config.client.request.expect.100.continue.processing</literal></entry>
- <entry>
- <para>
- Allows for HTTP Expect:100-Continue being handled by the HttpUrlConnector (default Jersey
- connector). <literal>Since 2.32</literal>
- </para>
- </entry>
- </row>
- <row>
- <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE;</entry>
- <entry><literal>jersey.config.client.request.expect.100.continue.threshold.size</literal></entry>
- <entry>
- <para>
- Property for threshold size for content length after which Expect:100-Continue header would be applied
- before the main request.
- Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
- the main request.
- <literal>Since 2.32</literal>
- </para>
- </entry>
- </row>
- <row>
<entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT;
</entry>
<entry>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 733df58..990f12a 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -239,6 +239,7 @@
<!ENTITY jaxrs.ext.ExceptionMapper "<link xlink:href='&jaxrs.javadoc.uri;/ext/ExceptionMapper.html'>ExceptionMapper<E extends Throwable></link>">
<!ENTITY jaxrs.ext.MessageBodyReader "<link xlink:href='&jaxrs.javadoc.uri;/ext/MessageBodyReader.html'>MessageBodyReader<T></link>">
<!ENTITY jaxrs.ext.MessageBodyWriter "<link xlink:href='&jaxrs.javadoc.uri;/ext/MessageBodyWriter.html'>MessageBodyWriter<T></link>">
+<!ENTITY jaxrs.ext.ParamConverter "<link xlink:href='&jaxrs.javadoc.uri;/ext/ParamConverter.html'>ParamConverter<T></link>">
<!ENTITY jaxrs.ext.Provider "<link xlink:href='&jaxrs.javadoc.uri;/ext/Provider.html'>@Provider</link>">
<!ENTITY jaxrs.ext.Providers "<link xlink:href='&jaxrs.javadoc.uri;/ext/Providers.html'>Providers</link>">
<!ENTITY jaxrs.ext.RuntimeDelegate "<link xlink:href='&jaxrs.javadoc.uri;/ext/RuntimeDelegate.html'>RuntimeDelegate</link>">
@@ -401,6 +402,7 @@
<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES</link>" >
<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT</link>" >
<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.PARAM_CONVERTERS_THROW_IAE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#PARAM_CONVERTERS_THROW_IAE'>CommonProperties.PARAM_CONVERTERS_THROW_IAE</link>" >
<!ENTITY jersey.common.internal.inject.DisposableSupplier "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/DisposableSupplier.html'>DisposableSupplier</link>">
<!ENTITY jersey.common.internal.inject.InjectionManager "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/InjectionManager.html'>InjectionManager</link>">
<!ENTITY jersey.common.internal.inject.AbstractBinder "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/AbstractBinder.html'>AbstractBinder</link>">
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java
new file mode 100644
index 0000000..b415cf8
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023 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.tests.e2e.common.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.internal.inject.Bindings;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ParamConverterConfigurator;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.glassfish.jersey.model.internal.RankedComparator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+import java.util.Date;
+
+public class ParamConvertersTest {
+ private CommonConfig config;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ config = new CommonConfig(null, ComponentBag.INCLUDE_ALL);
+ }
+
+ @Test
+ public void paramConvertersReturnNull() {
+ ParamConverter<String> converter = getParamConverter(String.class);
+ assertNull(String.class);
+ assertNull(Date.class);
+ assertNull(Character.class);
+ }
+
+ @Test
+ public void paramConvertersThrow() {
+ config.property(CommonProperties.PARAM_CONVERTERS_THROW_IAE, true);
+
+ assertThrow(String.class);
+ assertThrow(Date.class);
+ assertThrow(Character.class);
+ }
+
+ private <T> void assertNull(Class<T> clazz) {
+ ParamConverter<T> converter = getParamConverter(clazz);
+ Assertions.assertNotNull(converter);
+ Assertions.assertNull(converter.fromString(null));
+ Assertions.assertNull(converter.toString(null));
+ }
+
+ private <T> void assertThrow(Class<T> clazz) {
+ ParamConverter<T> converter = getParamConverter(clazz);
+ Assertions.assertNotNull(converter);
+
+ try {
+ converter.fromString(null);
+ throw new RuntimeException("The IAE was not thrown");
+ } catch (IllegalArgumentException illegalArgumentException) {
+ //expected
+ }
+
+ try {
+ converter.toString(null);
+ throw new RuntimeException("The IAE was not thrown");
+ } catch (IllegalArgumentException illegalArgumentException) {
+ //expected
+ }
+ }
+
+ private <T> ParamConverter<T> getParamConverter(Class<T> clazz) {
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ new ParamConverterConfigurator().init(injectionManager, null);
+ injectionManager.register(Bindings.service(config).to(Configuration.class));
+ injectionManager.completeRegistration();
+
+ final Iterable<ParamConverterProvider> allProviders =
+ Providers.getAllProviders(injectionManager, ParamConverterProvider.class, new RankedComparator<>());
+ for (ParamConverterProvider provider : allProviders) {
+ ParamConverter<T> converter = provider.getConverter(clazz, clazz, null);
+ if (converter != null) {
+ return converter;
+ }
+ }
+ return null;
+ }
+
+}