Skip to content

Commit 3667cef

Browse files
committed
fix: create new methods in BindingFactory instead of changing the existing ones, move bean ref logic to KafkaBeanRefHelper, implement BindingContext for passing Method and Class context to factories
1 parent c0dc484 commit 3667cef

File tree

26 files changed

+289
-144
lines changed

26 files changed

+289
-144
lines changed

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/BindingFactory.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,27 @@
66
import io.github.springwolf.asyncapi.v3.bindings.OperationBinding;
77
import io.github.springwolf.asyncapi.v3.model.ReferenceUtil;
88
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
9+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
910

1011
import java.util.Map;
1112

1213
public interface BindingFactory<T> {
13-
default String getChannelId(T annotation, Class<?> component) {
14-
return ReferenceUtil.toValidId(getChannelName(annotation, component));
14+
15+
// maintainer note: replaced by #getChannelId(T, BindingContext)
16+
default String getChannelId(T annotation) {
17+
return ReferenceUtil.toValidId(getChannelName(annotation));
1518
}
1619

17-
String getChannelName(T annotation, Class<?> component);
20+
// maintainer note: replaced by #getChannelName(T, BindingContext)
21+
String getChannelName(T annotation);
22+
23+
default String getChannelId(T annotation, BindingContext bindingContext) {
24+
return getChannelId(annotation);
25+
}
26+
27+
default String getChannelName(T annotation, BindingContext bindingContext) {
28+
return getChannelName(annotation);
29+
}
1830

1931
Map<String, ChannelBinding> buildChannelBinding(T annotation);
2032

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package io.github.springwolf.core.asyncapi.scanners.bindings.common;
3+
4+
import java.lang.reflect.Method;
5+
6+
public record BindingContext(Class<?> annotatedClass, Method annotatedMethod) {
7+
public BindingContext {
8+
if (annotatedClass == null && annotatedMethod == null) {
9+
throw new IllegalArgumentException("Either annotatedClass or annotatedMethod must be non-null");
10+
}
11+
}
12+
13+
public Class<?> getClassContext() {
14+
if (annotatedClass != null) {
15+
return annotatedClass;
16+
}
17+
if (annotatedMethod != null) {
18+
return annotatedMethod.getDeclaringClass();
19+
}
20+
21+
throw new IllegalStateException("Either annotatedClass or annotatedMethod must be non-null");
22+
}
23+
24+
public static BindingContext ofAnnotatedMethod(Method annotatedMethod) {
25+
return new BindingContext(null, annotatedMethod);
26+
}
27+
28+
public static BindingContext ofAnnotatedClass(Class<?> annotatedClass) {
29+
return new BindingContext(annotatedClass, null);
30+
}
31+
}

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject;
55
import io.github.springwolf.asyncapi.v3.model.channel.message.Message;
6+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
67
import io.github.springwolf.core.asyncapi.scanners.channels.ChannelsInClassScanner;
78
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
89
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
@@ -42,16 +43,17 @@ public List<ChannelObject> scan(Class<?> clazz) {
4243
private Stream<ChannelObject> mapClassToChannel(
4344
Class<?> component, Set<MethodAndAnnotation<MethodAnnotation>> annotatedMethods) {
4445
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);
46+
BindingContext bindingContext = BindingContext.ofAnnotatedClass(component);
4547
Set<Method> methods =
4648
annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet());
4749
Map<String, Message> messages = new HashMap<>(springAnnotationMessagesService.buildMessages(
48-
classAnnotation, component, methods, SpringAnnotationMessagesService.MessageType.CHANNEL));
50+
classAnnotation, bindingContext, methods, SpringAnnotationMessagesService.MessageType.CHANNEL));
4951

50-
return mapClassToChannel(classAnnotation, component, messages);
52+
return mapClassToChannel(classAnnotation, bindingContext, messages);
5153
}
5254

5355
private Stream<ChannelObject> mapClassToChannel(
54-
ClassAnnotation classAnnotation, Class<?> component, Map<String, Message> messages) {
55-
return Stream.of(springAnnotationChannelService.buildChannel(classAnnotation, component, messages));
56+
ClassAnnotation classAnnotation, BindingContext bindingContext, Map<String, Message> messages) {
57+
return Stream.of(springAnnotationChannelService.buildChannel(classAnnotation, bindingContext, messages));
5658
}
5759
}

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject;
77
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference;
88
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
9+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
910
import io.github.springwolf.core.asyncapi.scanners.channels.ChannelsInClassScanner;
1011
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
1112
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
@@ -48,7 +49,8 @@ private ChannelObject mapMethodToChannel(MethodAndAnnotation<MethodAnnotation> m
4849
MessageObject message = springAnnotationMessageService.buildMessage(annotation, payloadSchema, headerSchema);
4950
Map<String, Message> messages = Map.of(message.getMessageId(), MessageReference.toComponentMessage(message));
5051

51-
return springAnnotationChannelService.buildChannel(
52-
annotation, method.method().getDeclaringClass(), messages);
52+
BindingContext bindingContext = BindingContext.ofAnnotatedMethod(method.method());
53+
54+
return springAnnotationChannelService.buildChannel(annotation, bindingContext, messages);
5355
}
5456
}

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/channel/SpringAnnotationChannelService.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject;
77
import io.github.springwolf.asyncapi.v3.model.channel.message.Message;
88
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
9+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
910
import lombok.RequiredArgsConstructor;
1011
import lombok.extern.slf4j.Slf4j;
1112

@@ -18,10 +19,11 @@ public class SpringAnnotationChannelService<Annotation extends java.lang.annotat
1819

1920
private final BindingFactory<Annotation> bindingFactory;
2021

21-
public ChannelObject buildChannel(Annotation annotation, Class<?> component, Map<String, Message> messages) {
22+
public ChannelObject buildChannel(
23+
Annotation annotation, BindingContext bindingContext, Map<String, Message> messages) {
2224
Map<String, ChannelBinding> channelBinding = bindingFactory.buildChannelBinding(annotation);
2325
Map<String, ChannelBinding> chBinding = channelBinding != null ? new HashMap<>(channelBinding) : null;
24-
String channelName = bindingFactory.getChannelName(annotation, component);
26+
String channelName = bindingFactory.getChannelName(annotation, bindingContext);
2527
return ChannelObject.builder()
2628
.channelId(ReferenceUtil.toValidId(channelName))
2729
.address(channelName)

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
1111
import io.github.springwolf.core.asyncapi.components.ComponentsService;
1212
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
13+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
1314
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersBuilder;
1415
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderClassExtractor;
1516
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderSchemaObjectMerger;
@@ -43,13 +44,16 @@ public enum MessageType {
4344
}
4445

4546
public Map<String, MessageReference> buildMessages(
46-
ClassAnnotation classAnnotation, Class<?> component, Set<Method> methods, MessageType messageType) {
47+
ClassAnnotation classAnnotation,
48+
BindingContext bindingContext,
49+
Set<Method> methods,
50+
MessageType messageType) {
4751
Set<MessageObject> messages = methods.stream()
4852
.map(method -> buildMessage(classAnnotation, method))
4953
.collect(toSet());
5054

5155
if (messageType == MessageType.OPERATION) {
52-
String channelId = bindingFactory.getChannelName(classAnnotation, component);
56+
String channelId = bindingFactory.getChannelName(classAnnotation, bindingContext);
5357
return toOperationsMessagesMap(channelId, messages);
5458
}
5559
return toMessagesMap(messages);

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationService.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
1010
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
1111
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
12+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
1213
import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessageService;
1314
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject;
1415
import lombok.RequiredArgsConstructor;
@@ -26,13 +27,13 @@ public class SpringAnnotationOperationService<MethodAnnotation extends Annotatio
2627

2728
public Operation buildOperation(
2829
MethodAnnotation annotation,
29-
Class<?> component,
30+
BindingContext bindingContext,
3031
PayloadSchemaObject payloadType,
3132
SchemaObject headerSchema) {
3233
MessageObject message = springAnnotationMessageService.buildMessage(annotation, payloadType, headerSchema);
3334
Map<String, OperationBinding> operationBinding = bindingFactory.buildOperationBinding(annotation);
3435
Map<String, OperationBinding> opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null;
35-
String channelId = bindingFactory.getChannelId(annotation, component);
36+
String channelId = bindingFactory.getChannelId(annotation, bindingContext);
3637

3738
return Operation.builder()
3839
.action(OperationAction.RECEIVE)

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsService.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.github.springwolf.asyncapi.v3.model.operation.Operation;
99
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
1010
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
11+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
1112
import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessagesService;
1213
import lombok.RequiredArgsConstructor;
1314

@@ -23,17 +24,18 @@ public class SpringAnnotationOperationsService<ClassAnnotation extends Annotatio
2324
private final BindingFactory<ClassAnnotation> bindingFactory;
2425
private final SpringAnnotationMessagesService<ClassAnnotation> springAnnotationMessagesService;
2526

26-
public Operation buildOperation(ClassAnnotation classAnnotation, Class<?> component, Set<Method> methods) {
27+
public Operation buildOperation(
28+
ClassAnnotation classAnnotation, BindingContext bindingContext, Set<Method> methods) {
2729
var messages = springAnnotationMessagesService.buildMessages(
28-
classAnnotation, component, methods, SpringAnnotationMessagesService.MessageType.OPERATION);
29-
return buildOperation(classAnnotation, component, messages);
30+
classAnnotation, bindingContext, methods, SpringAnnotationMessagesService.MessageType.OPERATION);
31+
return buildOperation(classAnnotation, bindingContext, messages);
3032
}
3133

3234
private Operation buildOperation(
33-
ClassAnnotation classAnnotation, Class<?> component, Map<String, MessageReference> messages) {
35+
ClassAnnotation classAnnotation, BindingContext bindingContext, Map<String, MessageReference> messages) {
3436
Map<String, OperationBinding> operationBinding = bindingFactory.buildOperationBinding(classAnnotation);
3537
Map<String, OperationBinding> opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null;
36-
String channelName = bindingFactory.getChannelName(classAnnotation, component);
38+
String channelName = bindingFactory.getChannelName(classAnnotation, bindingContext);
3739
String channelId = ReferenceUtil.toValidId(channelName);
3840

3941
return Operation.builder()

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.github.springwolf.asyncapi.v3.model.operation.Operation;
55
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
66
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
7+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
78
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
89
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
910
import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation;
@@ -42,14 +43,16 @@ public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
4243
private Stream<Map.Entry<String, Operation>> mapClassToOperation(
4344
Class<?> component, Set<MethodAndAnnotation<MethodAnnotation>> annotatedMethods) {
4445
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);
46+
BindingContext bindingContext = BindingContext.ofAnnotatedClass(component);
4547

46-
String channelId = bindingFactory.getChannelId(classAnnotation, component);
48+
String channelId = bindingFactory.getChannelId(classAnnotation, bindingContext);
4749
String operationId =
4850
StringUtils.joinWith("_", channelId, OperationAction.RECEIVE.type, component.getSimpleName());
4951

5052
Set<Method> methods =
5153
annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet());
52-
Operation operation = springAnnotationOperationsService.buildOperation(classAnnotation, component, methods);
54+
Operation operation =
55+
springAnnotationOperationsService.buildOperation(classAnnotation, bindingContext, methods);
5356
annotatedMethods.forEach(
5457
method -> customizers.forEach(customizer -> customizer.customize(operation, method.method())));
5558
return Stream.of(Map.entry(operationId, operation));

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
66
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
77
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
8+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
89
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
910
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
1011
import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation;
@@ -42,17 +43,17 @@ public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
4243

4344
private Map.Entry<String, Operation> mapMethodToOperation(MethodAndAnnotation<MethodAnnotation> method) {
4445
MethodAnnotation annotation = AnnotationUtil.findFirstAnnotationOrThrow(methodAnnotationClass, method.method());
46+
BindingContext bindingContext = BindingContext.ofAnnotatedMethod(method.method());
4547

46-
String channelId =
47-
bindingFactory.getChannelId(annotation, method.method().getDeclaringClass());
48+
String channelId = bindingFactory.getChannelId(annotation, bindingContext);
4849
String operationId = StringUtils.joinWith(
4950
"_", channelId, OperationAction.RECEIVE.type, method.method().getName());
5051

5152
PayloadSchemaObject payloadSchema = payloadMethodParameterService.extractSchema(method.method());
5253
SchemaObject headerSchema = headerClassExtractor.extractHeader(method.method(), payloadSchema);
5354

5455
Operation operation = springAnnotationOperationService.buildOperation(
55-
annotation, method.method().getDeclaringClass(), payloadSchema, headerSchema);
56+
annotation, bindingContext, payloadSchema, headerSchema);
5657
customizers.forEach(customizer -> customizer.customize(operation, method.method()));
5758
return Map.entry(operationId, operation);
5859
}

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ static class TestBindingFactory implements BindingFactory<TestClassListener> {
291291
Map.of(CHANNEL_ID, new TestBindingFactory.TestOperationBinding());
292292

293293
@Override
294-
public String getChannelName(TestClassListener annotation, Class<?> component) {
294+
public String getChannelName(TestClassListener annotation) {
295295
return CHANNEL;
296296
}
297297

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScannerIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ static class TestBindingFactory implements BindingFactory<TestChannelListener> {
311311
Map.of(CHANNEL_ID, new TestBindingFactory.TestOperationBinding());
312312

313313
@Override
314-
public String getChannelName(TestChannelListener annotation, Class<?> component) {
314+
public String getChannelName(TestChannelListener annotation) {
315315
return CHANNEL;
316316
}
317317

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationServiceTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.github.springwolf.asyncapi.v3.model.operation.OperationAction;
1111
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
1212
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
13+
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
1314
import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessageService;
1415
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject;
1516
import org.junit.jupiter.api.BeforeEach;
@@ -60,7 +61,10 @@ void scan_componentHasTestListenerMethods() throws NoSuchMethodException {
6061

6162
// when
6263
Operation operations = springAnnotationOperationService.buildOperation(
63-
annotation, ClassWithTestListenerAnnotation.class, payloadSchemaName, headerSchema);
64+
annotation,
65+
BindingContext.ofAnnotatedClass(ClassWithTestListenerAnnotation.class),
66+
payloadSchemaName,
67+
headerSchema);
6468

6569
// then
6670
Operation expectedOperation = Operation.builder()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package io.github.springwolf.examples.kafka.consumers;
3+
4+
import io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto;
5+
import io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto;
6+
import io.github.springwolf.examples.kafka.producers.AnotherProducer;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.kafka.annotation.KafkaListener;
10+
import org.springframework.kafka.support.KafkaHeaders;
11+
import org.springframework.messaging.handler.annotation.Header;
12+
import org.springframework.messaging.handler.annotation.Payload;
13+
import org.springframework.stereotype.Component;
14+
15+
import java.util.List;
16+
17+
@Component
18+
@RequiredArgsConstructor
19+
@Slf4j
20+
public class ExampleBeanRefKafkaListener {
21+
22+
@SuppressWarnings("unused")
23+
public final String TOPIC_NAME = "example-topic-from-bean-ref";
24+
25+
private final AnotherProducer anotherProducer;
26+
27+
@KafkaListener(topics = "#{myListener.TOPIC_NAME}", beanRef = "myListener")
28+
public void receiveExamplePayload(
29+
@Header(KafkaHeaders.RECEIVED_KEY) String key,
30+
@Header(KafkaHeaders.OFFSET) Integer offset,
31+
@Payload ExamplePayloadDto payload) {
32+
log.info("Received new message in example-topic: {}", payload.toString());
33+
34+
AnotherPayloadDto example = new AnotherPayloadDto();
35+
example.setExample(payload);
36+
example.setFoo("foo");
37+
38+
anotherProducer.sendMessage(example);
39+
}
40+
41+
@KafkaListener(topicPattern = "another-topic", groupId = "example-group-id", batch = "true")
42+
public void receiveAnotherPayloadBatched(List<AnotherPayloadDto> payloads) {
43+
log.info("Received new message in another-topic: {}", payloads.toString());
44+
}
45+
}

springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/AmqpBindingFactory.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ public AmqpBindingFactory(
2929
}
3030

3131
@Override
32-
public String getChannelName(RabbitListener annotation, Class<?> component) {
32+
public String getChannelName(RabbitListener annotation) {
3333
return RabbitListenerUtil.getChannelName(annotation, stringValueResolver);
3434
}
3535

3636
@Override
37-
public String getChannelId(RabbitListener annotation, Class<?> component) {
37+
public String getChannelId(RabbitListener annotation) {
3838
return RabbitListenerUtil.getChannelId(annotation, stringValueResolver);
3939
}
4040

0 commit comments

Comments
 (0)