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

Prohibit the use of legacy Kotlin interface default methods on Reactive REST Client #22276

Merged
merged 1 commit into from
Jan 4, 2022
Merged
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 @@ -28,6 +28,7 @@

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.eclipse.microprofile.rest.client.inject.RestClient;
Expand Down Expand Up @@ -88,8 +89,10 @@ class RestClientReactiveProcessor {

private static final DotName REGISTER_REST_CLIENT = DotName.createSimple(RegisterRestClient.class.getName());
private static final DotName SESSION_SCOPED = DotName.createSimple(SessionScoped.class.getName());
private static final DotName KOTLIN_METADATA_ANNOTATION = DotName.createSimple("kotlin.Metadata");

private static final String DISABLE_SMART_PRODUCES_QUARKUS = "quarkus.rest-client.disable-smart-produces";
private static final String KOTLIN_INTERFACE_DEFAULT_IMPL_SUFFIX = "$DefaultImpls";

@BuildStep
void announceFeature(BuildProducer<FeatureBuildItem> features) {
Expand Down Expand Up @@ -339,6 +342,8 @@ void addRestClientBeans(Capabilities capabilities,
ClassInfo jaxrsInterface = registerRestClient.target().asClass();
// for each interface annotated with @RegisterRestClient, generate a $$CDIWrapper CDI bean that can be injected
if (Modifier.isAbstract(jaxrsInterface.flags())) {
validateKotlinDefaultMethods(jaxrsInterface, index);

List<MethodInfo> methodsToImplement = new ArrayList<>();

// search this interface and its super interfaces for jaxrs methods
Expand Down Expand Up @@ -453,6 +458,22 @@ void addRestClientBeans(Capabilities capabilities,
}
}

// By default, Kotlin does not use Java interface default methods, but generates a helper class that contains the implementation.
// In order to avoid the extra complexity of having to deal with this mode, we simply fail the build when this situation is encountered
// and provide an actionable error message on how to remedy the situation.
private void validateKotlinDefaultMethods(ClassInfo jaxrsInterface, IndexView index) {
if (jaxrsInterface.classAnnotation(KOTLIN_METADATA_ANNOTATION) != null) {
var potentialDefaultImplClass = DotName
.createSimple(jaxrsInterface.name().toString() + KOTLIN_INTERFACE_DEFAULT_IMPL_SUFFIX);
if (index.getClassByName(potentialDefaultImplClass) != null) {
throw new RestClientDefinitionException(String.format(
"Using Kotlin default methods on interfaces that are not backed by Java 8 default interface methods is not supported. See %s for more details. Offending interface is '%s'.",
"https://kotlinlang.org/docs/java-to-kotlin-interop.html#default-methods-in-interfaces",
jaxrsInterface.name().toString()));
}
}
}

private boolean isRestMethod(MethodInfo method) {
if (!Modifier.isAbstract(method.flags())) {
return false;
Expand Down