Skip to content

refactor(cloud-stream): FunctionalChannelBeanBuilder fails fast if An… #1272

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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 @@ -2,6 +2,9 @@
package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common;

import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ResolvableType;

Expand All @@ -12,6 +15,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
Expand All @@ -24,139 +28,163 @@ public class FunctionalChannelBeanBuilder {
private final TypeExtractor typeExtractor;

public Set<FunctionalChannelBeanData> build(AnnotatedElement element) {
Class<?> type = getRawType(element);
final AnnotatedClassOrMethod annotatedClassOrMethod = getAnnotatedClassOrMethodOrThrow(element, typeExtractor);

Class<?> type = annotatedClassOrMethod.getRawType();

if (Consumer.class.isAssignableFrom(type) || BiConsumer.class.isAssignableFrom(type)) {
List<Type> typeGenerics = getTypeGenerics(element);
List<Type> typeGenerics = annotatedClassOrMethod.getTypeGenerics();
if (typeGenerics.isEmpty()) {
return Collections.emptySet();
}
Type payloadType = typeGenerics.get(0);
return Set.of(ofConsumer(element, payloadType));
return Set.of(ofConsumer(annotatedClassOrMethod, payloadType));
}

if (Supplier.class.isAssignableFrom(type)) {
List<Type> typeGenerics = getTypeGenerics(element);
List<Type> typeGenerics = annotatedClassOrMethod.getTypeGenerics();
if (typeGenerics.isEmpty()) {
return Collections.emptySet();
}
Type payloadType = typeGenerics.get(0);
return Set.of(ofSupplier(element, payloadType));
return Set.of(ofSupplier(annotatedClassOrMethod, payloadType));
}

if (Function.class.isAssignableFrom(type)) {
List<Type> typeGenerics = getTypeGenerics(element);
List<Type> typeGenerics = annotatedClassOrMethod.getTypeGenerics();
if (typeGenerics.size() != 2) {
return Collections.emptySet();
}
Type inputType = typeGenerics.get(0);
Type outputType = typeGenerics.get(1);

return Set.of(ofConsumer(element, inputType), ofSupplier(element, outputType));
return Set.of(
ofConsumer(annotatedClassOrMethod, inputType), ofSupplier(annotatedClassOrMethod, outputType));
}

if (BiFunction.class.isAssignableFrom(type)) {
List<Type> typeGenerics = getTypeGenerics(element);
List<Type> typeGenerics = annotatedClassOrMethod.getTypeGenerics();
if (typeGenerics.size() != 3) {
return Collections.emptySet();
}
Type inputType = typeGenerics.get(0);
Type outputType = typeGenerics.get(2);

return Set.of(ofConsumer(element, inputType), ofSupplier(element, outputType));
return Set.of(
ofConsumer(annotatedClassOrMethod, inputType), ofSupplier(annotatedClassOrMethod, outputType));
}

return Collections.emptySet();
}

private static Class<?> getRawType(AnnotatedElement element) {
if (element instanceof Method m) {
return m.getReturnType();
private static AnnotatedClassOrMethod getAnnotatedClassOrMethodOrThrow(
AnnotatedElement annotatedElement, TypeExtractor typeExtractor) {
if (annotatedElement instanceof Method m) {
return AnnotatedClassOrMethod.forMethod(annotatedElement, m, typeExtractor);
}

if (element instanceof Class<?> c) {
return c;
if (annotatedElement instanceof Class<?> c) {
return AnnotatedClassOrMethod.forClass(annotatedElement, c, typeExtractor);
}

throw new IllegalArgumentException("Must be a Method or Class");
}

private static FunctionalChannelBeanData ofConsumer(AnnotatedElement element, Type payloadType) {
String name = getElementName(element);
private static FunctionalChannelBeanData ofConsumer(
AnnotatedClassOrMethod annotatedClassOrMethod, Type payloadType) {
String name = annotatedClassOrMethod.getName();
String cloudStreamBinding = firstCharToLowerCase(name) + "-in-0";
return new FunctionalChannelBeanData(
name, element, payloadType, FunctionalChannelBeanData.BeanType.CONSUMER, cloudStreamBinding);
name,
annotatedClassOrMethod.annotatedElement,
payloadType,
FunctionalChannelBeanData.BeanType.CONSUMER,
cloudStreamBinding);
}

private static FunctionalChannelBeanData ofSupplier(AnnotatedElement element, Type payloadType) {
String name = getElementName(element);
private static FunctionalChannelBeanData ofSupplier(
AnnotatedClassOrMethod annotatedClassOrMethod, Type payloadType) {
String name = annotatedClassOrMethod.getName();
String cloudStreamBinding = firstCharToLowerCase(name) + "-out-0";
return new FunctionalChannelBeanData(
name, element, payloadType, FunctionalChannelBeanData.BeanType.SUPPLIER, cloudStreamBinding);
name,
annotatedClassOrMethod.annotatedElement,
payloadType,
FunctionalChannelBeanData.BeanType.SUPPLIER,
cloudStreamBinding);
}

private static String firstCharToLowerCase(String name) {
return name.substring(0, 1).toLowerCase() + name.substring(1);
}

private static String getElementName(AnnotatedElement element) {
if (element instanceof Method m) {
return m.getName();
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
private static class AnnotatedClassOrMethod {

private final AnnotatedElement annotatedElement;
private final String name;
private final Class<?> rawType;
private final List<Type> typeGenerics;

public static AnnotatedClassOrMethod forClass(
AnnotatedElement element, Class<?> clazz, TypeExtractor typeExtractor) {
return new AnnotatedClassOrMethod(
element, clazz.getSimpleName(), clazz, getTypeGenericsForClass(clazz, typeExtractor));
}

if (element instanceof Class<?> c) {
return c.getSimpleName();
public static AnnotatedClassOrMethod forMethod(
AnnotatedElement element, Method method, TypeExtractor typeExtractor) {
return new AnnotatedClassOrMethod(
element, method.getName(), method.getReturnType(), getTypeGenericsForMethod(method, typeExtractor));
}

throw new IllegalArgumentException("Must be a Method or Class");
}
private static List<Type> getTypeGenericsForClass(Class<?> clazz, TypeExtractor typeExtractor) {

private List<Type> getTypeGenerics(AnnotatedElement element) {
if (element instanceof Method m) {
Type returnType = getMethodReturnType(m);
if (returnType instanceof ParameterizedType) {
return getTypeGenerics(((ParameterizedType) returnType));
ResolvableType resolvableType = ResolvableType.forClass(clazz);
Optional<Type> type = getParameterizedType(resolvableType);
if (type.isPresent() && type.get() instanceof ParameterizedType) {
return getTypeGenerics((ParameterizedType) type.get(), typeExtractor);
} else {
return Collections.emptyList();
}
}

if (element instanceof Class<?> c) {
ResolvableType resolvableType = ResolvableType.forClass(c);
Type type = getParameterizedType(resolvableType);
if (type instanceof ParameterizedType) {
return getTypeGenerics((ParameterizedType) type);
private static List<Type> getTypeGenericsForMethod(Method method, TypeExtractor typeExtractor) {

Optional<Type> returnType = getMethodReturnType(method);
if (returnType.isPresent() && returnType.get() instanceof ParameterizedType) {
return getTypeGenerics(((ParameterizedType) returnType.get()), typeExtractor);
} else {
return Collections.emptyList();
}
}

throw new IllegalArgumentException("Must be a Method or Class");
}

private Type getMethodReturnType(Method m) {
ResolvableType resolvableType = ResolvableType.forMethodReturnType(m);
return getParameterizedType(resolvableType);
}
private static List<Type> getTypeGenerics(ParameterizedType parameterizedType, TypeExtractor typeExtractor) {
return Arrays.stream(parameterizedType.getActualTypeArguments())
.map(typeExtractor::extractActualType)
.toList();
}

private Type getParameterizedType(ResolvableType resolvableType) {
if (Consumer.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return resolvableType.as(Consumer.class).getType();
} else if (BiConsumer.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return resolvableType.as(BiConsumer.class).getType();
} else if (Supplier.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return resolvableType.as(Supplier.class).getType();
} else if (Function.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return resolvableType.as(Function.class).getType();
} else if (BiFunction.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return resolvableType.as(BiFunction.class).getType();
private static Optional<Type> getParameterizedType(ResolvableType resolvableType) {

if (Consumer.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return Optional.of(resolvableType.as(Consumer.class).getType());
} else if (BiConsumer.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return Optional.of(resolvableType.as(BiConsumer.class).getType());
} else if (Supplier.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return Optional.of(resolvableType.as(Supplier.class).getType());
} else if (Function.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return Optional.of(resolvableType.as(Function.class).getType());
} else if (BiFunction.class.isAssignableFrom(resolvableType.resolve(Object.class))) {
return Optional.of(resolvableType.as(BiFunction.class).getType());
}
return Optional.empty();
}
throw new IllegalArgumentException("Illegal type: " + resolvableType.getRawClass());
}

private List<Type> getTypeGenerics(ParameterizedType parameterizedType) {
return Arrays.stream(parameterizedType.getActualTypeArguments())
.map(typeExtractor::extractActualType)
.toList();
private static Optional<Type> getMethodReturnType(Method m) {
ResolvableType resolvableType = ResolvableType.forMethodReturnType(m);
return getParameterizedType(resolvableType);
}
}
}
Loading
Loading