Skip to content

Commit

Permalink
Parametrize ParamConverters to allow throwing IAE (#5349)
Browse files Browse the repository at this point in the history
* Parametrize ParamConverters to allow throwing IAE

Signed-off-by: jansupol <[email protected]>
  • Loading branch information
jansupol authored Jun 20, 2023
1 parent 635d16d commit 1f0dbfa
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -304,6 +304,22 @@ public final class CommonProperties {
*/
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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -85,7 +107,7 @@ public T fromString(final String value) {
@Override
public String toString(final T value) throws IllegalArgumentException {
if (value == null) {
return null;
return nullOrThrow();
}
return value.toString();
}
Expand All @@ -97,7 +119,11 @@ public String toString(final T value) throws IllegalArgumentException {
* 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,
Expand All @@ -106,7 +132,7 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,

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 {
Expand All @@ -122,7 +148,11 @@ protected T _fromString(final String value) throws Exception {
* 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,
Expand All @@ -131,7 +161,7 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,

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 {
Expand All @@ -146,7 +176,11 @@ public T _fromString(final String value) throws Exception {
* 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,
Expand All @@ -155,7 +189,7 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,

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 {
Expand All @@ -172,6 +206,10 @@ public T _fromString(final String value) throws Exception {
@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,
Expand All @@ -181,7 +219,11 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,
}

@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,
Expand All @@ -192,7 +234,7 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@Override
public T fromString(String value) {
if (value == null || value.isEmpty()) {
return null;
return CharacterProvider.this.nullOrThrow();
}

if (value.length() == 1) {
Expand All @@ -205,7 +247,7 @@ public T fromString(String value) {
@Override
public String toString(T value) {
if (value == null) {
return null;
return CharacterProvider.this.nullOrThrow();
}
return value.toString();
}
Expand All @@ -222,7 +264,11 @@ public String toString(T value) {
* {@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,
Expand All @@ -233,7 +279,7 @@ public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@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));
Expand All @@ -245,7 +291,7 @@ public T fromString(final String value) {
@Override
public String toString(final T value) throws IllegalArgumentException {
if (value == null) {
return null;
return DateProvider.this.nullOrThrow();
}
return value.toString();
}
Expand All @@ -258,12 +304,13 @@ public String toString(final T value) throws IllegalArgumentException {
* 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;
}

Expand Down Expand Up @@ -293,7 +340,7 @@ public T fromString(String value) {
* 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();
}
}

Expand Down Expand Up @@ -408,17 +455,20 @@ public static class AggregatedProvider implements ParamConverterProvider {
* 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()
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -284,8 +287,9 @@ public void testLazyConverter() throws Exception {
@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");
Expand Down
Loading

0 comments on commit 1f0dbfa

Please sign in to comment.