forked from springwolf/springwolf-core
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: interceptor filter pattern for serialization/deserialization co…
…mmon cases such as Message and KStream (springwolf#441) * feat: added test for spring messaging consumer bean * added implmentation to extract instance of class * refactor(core): use 1024mb of heap for test execution Co-authored-by: Timon Back <[email protected]> * refactor(core): extract SpringPayloadAnnotationTypeExtractor as spring bean Co-authored-by: Timon Back <[email protected]> * refactor(core): WIP add ListExtractor Co-authored-by: Timon Back <[email protected]> * refactor(core): WIP add ListExtractor Co-authored-by: Timon Back <[email protected]> * refactor: extract different types of generic payloads * refactor: handle KStream with positional generic element correctly (wip) * feat: handle nested generic arguments * refactor: extract FunctionalChannelBeanBuilder * refactor(core): extract TypeToClassConverter * refactor(core): rename SpringPayloadAnnotationTypeExtractor to PayloadClassExtractor * chore: fix code after rebase to springwolf/master * feat(core): configure payload classes via properties * feat(core)!: payload of type List is not extracted by default anymore * feat(core): add option to disable default extractable classes Co-authored-by: Timon Back <[email protected]> --------- Co-authored-by: Sheheryar <[email protected]> Co-authored-by: David Müller <[email protected]> Co-authored-by: Timon Back <[email protected]>
- Loading branch information
1 parent
5d14776
commit 6b7d4b0
Showing
53 changed files
with
894 additions
and
461 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 0 additions & 80 deletions
80
...pringwolf/asyncapi/scanners/channels/annotation/SpringPayloadAnnotationTypeExtractor.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
...ithub/stavshamir/springwolf/asyncapi/scanners/channels/payload/PayloadClassExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload; | ||
|
||
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.messaging.handler.annotation.Payload; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.ParameterizedType; | ||
import java.lang.reflect.Type; | ||
import java.util.Arrays; | ||
import java.util.Map; | ||
|
||
@Slf4j | ||
public class PayloadClassExtractor { | ||
private final TypeToClassConverter typeToClassConverter; | ||
|
||
public PayloadClassExtractor(SpringwolfConfigProperties properties) { | ||
Map<String, Integer> extractableClasses = Map.of(); | ||
if (properties.getPayload() != null) { | ||
extractableClasses = properties.getPayload().getExtractableClasses(); | ||
} | ||
typeToClassConverter = new TypeToClassConverter(extractableClasses); | ||
} | ||
|
||
public Class<?> extractFrom(Method method) { | ||
String methodName = String.format("%s::%s", method.getDeclaringClass().getSimpleName(), method.getName()); | ||
log.debug("Finding payload type for {}", methodName); | ||
|
||
int parameterPayloadIndex = | ||
getPayloadParameterIndex(method.getParameterTypes(), method.getParameterAnnotations(), methodName); | ||
|
||
return typeToClassConverter.extractClass(method.getGenericParameterTypes()[parameterPayloadIndex]); | ||
} | ||
|
||
public Class<?> typeToClass(Type type) { | ||
return typeToClassConverter.extractClass(type); | ||
} | ||
|
||
private int getPayloadParameterIndex( | ||
Class<?>[] parameterClasses, Annotation[][] parameterAnnotations, String methodName) { | ||
switch (parameterClasses.length) { | ||
case 0 -> throw new IllegalArgumentException("Listener methods must not have 0 parameters: " + methodName); | ||
case 1 -> { | ||
return 0; | ||
} | ||
default -> { | ||
int payloadAnnotatedParameterIndex = getPayloadAnnotatedParameterIndex(parameterAnnotations); | ||
if (payloadAnnotatedParameterIndex == -1) { | ||
String msg = | ||
"Multi-parameter AsyncListener methods must have one parameter annotated with @Payload, " | ||
+ "but none was found: " | ||
+ methodName; | ||
|
||
throw new IllegalArgumentException(msg); | ||
} | ||
return payloadAnnotatedParameterIndex; | ||
} | ||
} | ||
} | ||
|
||
private int getPayloadAnnotatedParameterIndex(Annotation[][] parameterAnnotations) { | ||
for (int i = 0, length = parameterAnnotations.length; i < length; i++) { | ||
Annotation[] annotations = parameterAnnotations[i]; | ||
boolean hasPayloadAnnotation = Arrays.stream(annotations).anyMatch(Payload.class::isInstance); | ||
|
||
if (hasPayloadAnnotation) { | ||
return i; | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
@RequiredArgsConstructor | ||
private static class TypeToClassConverter { | ||
|
||
private final Map<String, Integer> extractableClassToArgumentIndex; | ||
|
||
private Class<?> extractClass(Type parameterType) { | ||
try { | ||
if (parameterType instanceof ParameterizedType) { | ||
Type rawParameterType = ((ParameterizedType) parameterType).getRawType(); | ||
String rawParameterTypeName = rawParameterType.getTypeName(); | ||
|
||
Class<?> actualPayloadClass = | ||
extractActualGenericClass((ParameterizedType) parameterType, rawParameterTypeName); | ||
if (actualPayloadClass != Void.class) { | ||
return actualPayloadClass; | ||
} | ||
|
||
// nested generic class - fallback to most outer container | ||
return Class.forName(rawParameterTypeName); | ||
} | ||
|
||
// no generics used - just a normal type | ||
return Class.forName(parameterType.getTypeName()); | ||
} catch (Exception ex) { | ||
log.info("Unable to extract generic data type of %s".formatted(parameterType), ex); | ||
} | ||
return Void.class; | ||
} | ||
|
||
private Class<?> extractActualGenericClass(ParameterizedType parameterType, String rawParameterTypeName) { | ||
Type type = parameterType; | ||
String typeName = rawParameterTypeName; | ||
|
||
while (type instanceof ParameterizedType && extractableClassToArgumentIndex.containsKey(typeName)) { | ||
Integer index = extractableClassToArgumentIndex.get(rawParameterTypeName); | ||
|
||
type = ((ParameterizedType) type).getActualTypeArguments()[index]; | ||
|
||
typeName = type.getTypeName(); | ||
if (type instanceof ParameterizedType) { | ||
typeName = ((ParameterizedType) type).getRawType().getTypeName(); | ||
} | ||
} | ||
|
||
try { | ||
return Class.forName(typeName); | ||
} catch (ClassNotFoundException ex) { | ||
log.debug("Unable to find class for type %s".formatted(typeName), ex); | ||
} | ||
|
||
return Void.class; | ||
} | ||
} | ||
} |
Oops, something went wrong.