Remove setSchema API from templates
For both PulsarTemplate and ReactivePulsarTemplate: * remove setSchema() * add schema param in simple API * add schema param in fluent API Update all tests and samples accordingly See #268
This commit is contained in:
@@ -1,19 +1,12 @@
|
||||
== Specifying Schema Information
|
||||
If you use Java primitive types, the framework auto-detects the schema for you, and you need not specify any schema types for publishing the data.
|
||||
However, if you use any complex types (such as `JSON`, `AVRO`, `PROTOBUF`, and others), you need to set the proper schema type on the `{template-class}` before invoking any send operations, as the following example shows for JSON:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
template.setSchema(Schema.JSON(Foo.class));
|
||||
----
|
||||
====
|
||||
However, if you use any complex types (such as `JSON`, `AVRO`, `PROTOBUF`, and others), you need to specify the proper schema when invoking send operations on the `{template-class}`.
|
||||
|
||||
IMPORTANT: Complex Schema types that are currently supported are JSON, AVRO, PROTOBUF, and KEY_VALUE w/ INLINE encoding.
|
||||
|
||||
=== Custom Schema Mapping
|
||||
As an alternative to specifying the schema on the `{template-class}` for complex types, the schema resolver can be configured with mappings for the types.
|
||||
This removes the need to set the schema on the template as the framework consults the resolver using the outgoing message type.
|
||||
As an alternative to specifying the schema when invoking send operations on the `{template-class}` for complex types, the schema resolver can be configured with mappings for the types.
|
||||
This removes the need to specify the schema as the framework consults the resolver using the outgoing message type.
|
||||
|
||||
The following example shows a schema resolver customizer that adds mappings for the `User` and `Address` complex objects using `AVRO` and `JSON` schemas, respectively:
|
||||
|
||||
@@ -29,11 +22,4 @@ public SchemaResolverCustomizer<DefaultSchemaResolver> schemaResolverCustomizer(
|
||||
}
|
||||
----
|
||||
====
|
||||
With this configuration in place, there is no need to set the schema on the template, for example:
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
template.send("user-topic", someUserObject);
|
||||
template.send("address-topic", someAddressObject);
|
||||
----
|
||||
====
|
||||
With this configuration in place, there is no need to set specify the schema on send operations.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.Optional;
|
||||
|
||||
import org.apache.pulsar.reactive.client.api.ReactiveMessageSenderSpec;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ final class ReactiveMessageSenderUtils {
|
||||
private ReactiveMessageSenderUtils() {
|
||||
}
|
||||
|
||||
static <T> String resolveTopicName(String userSpecifiedTopic,
|
||||
static <T> String resolveTopicName(@Nullable String userSpecifiedTopic,
|
||||
ReactivePulsarSenderFactory<T> reactiveMessageSenderFactory) {
|
||||
ReactiveMessageSenderSpec reactiveMessageSenderSpec = reactiveMessageSenderFactory
|
||||
.getReactiveMessageSenderSpec();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,11 @@
|
||||
package org.springframework.pulsar.reactive.core;
|
||||
|
||||
import org.apache.pulsar.client.api.MessageId;
|
||||
import org.apache.pulsar.client.api.Schema;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -27,6 +30,7 @@ import reactor.core.publisher.Mono;
|
||||
*
|
||||
* @param <T> the message payload type
|
||||
* @author Christophe Bornet
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public interface ReactivePulsarOperations<T> {
|
||||
|
||||
@@ -37,6 +41,15 @@ public interface ReactivePulsarOperations<T> {
|
||||
*/
|
||||
Mono<MessageId> send(T message);
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a reactive manner. default topic
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to use the default schema
|
||||
* resolution
|
||||
* @return the id assigned by the broker to the published message
|
||||
*/
|
||||
Mono<MessageId> send(T message, @Nullable Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a reactive manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
@@ -44,7 +57,18 @@ public interface ReactivePulsarOperations<T> {
|
||||
* @param message the message to send
|
||||
* @return the id assigned by the broker to the published message
|
||||
*/
|
||||
Mono<MessageId> send(String topic, T message);
|
||||
Mono<MessageId> send(@Nullable String topic, T message);
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a reactive manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
* default topic
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to use the default schema
|
||||
* resolution
|
||||
* @return the id assigned by the broker to the published message
|
||||
*/
|
||||
Mono<MessageId> send(@Nullable String topic, T message, @Nullable Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Sends multiple messages to the default topic in a reactive manner.
|
||||
@@ -54,6 +78,16 @@ public interface ReactivePulsarOperations<T> {
|
||||
*/
|
||||
Flux<MessageId> send(Publisher<T> messages);
|
||||
|
||||
/**
|
||||
* Sends multiple messages to the default topic in a reactive manner.
|
||||
* @param messages the messages to send
|
||||
* @param schema the schema to use or {@code null} to use the default schema
|
||||
* resolution
|
||||
* @return the ids assigned by the broker to the published messages in the same order
|
||||
* as they were sent
|
||||
*/
|
||||
Flux<MessageId> send(Publisher<T> messages, @Nullable Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Sends multiple messages to the specified topic in a reactive manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
@@ -62,7 +96,19 @@ public interface ReactivePulsarOperations<T> {
|
||||
* @return the ids assigned by the broker to the published messages in the same order
|
||||
* as they were sent
|
||||
*/
|
||||
Flux<MessageId> send(String topic, Publisher<T> messages);
|
||||
Flux<MessageId> send(@Nullable String topic, Publisher<T> messages);
|
||||
|
||||
/**
|
||||
* Sends multiple messages to the specified topic in a reactive manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
* default topic
|
||||
* @param messages the messages to send
|
||||
* @param schema the schema to use or {@code null} to use the default schema
|
||||
* resolution
|
||||
* @return the ids assigned by the broker to the published messages in the same order
|
||||
* as they were sent
|
||||
*/
|
||||
Flux<MessageId> send(@Nullable String topic, Publisher<T> messages, @Nullable Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Create a {@link SendMessageBuilder builder} for configuring and sending a message
|
||||
@@ -72,6 +118,14 @@ public interface ReactivePulsarOperations<T> {
|
||||
*/
|
||||
SendMessageBuilder<T> newMessage(T message);
|
||||
|
||||
/**
|
||||
* Create a {@link SendMessageBuilder builder} for configuring and sending multiple
|
||||
* messages reactively.
|
||||
* @param messages the messages to send
|
||||
* @return the builder to configure and send the message
|
||||
*/
|
||||
SendMessageBuilder<T> newMessages(Publisher<T> messages);
|
||||
|
||||
/**
|
||||
* Builder that can be used to configure and send a message. Provides more options
|
||||
* than the send methods provided by {@link ReactivePulsarOperations}.
|
||||
@@ -87,6 +141,13 @@ public interface ReactivePulsarOperations<T> {
|
||||
*/
|
||||
SendMessageBuilder<T> withTopic(String topic);
|
||||
|
||||
/**
|
||||
* Specify the schema to use when sending the message.
|
||||
* @param schema the schema to use
|
||||
* @return the current builder with the schema specified
|
||||
*/
|
||||
SendMessageBuilder<T> withSchema(Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Specifies the message customizer to use to further configure the message.
|
||||
* @param customizer the message customizer
|
||||
@@ -109,6 +170,13 @@ public interface ReactivePulsarOperations<T> {
|
||||
*/
|
||||
Mono<MessageId> send();
|
||||
|
||||
/**
|
||||
* Send the messages in a reactive manner using the configured specification.
|
||||
* @return the ids assigned by the broker to the published messages in the same
|
||||
* order as they were sent
|
||||
*/
|
||||
Flux<MessageId> sendMany();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* A thread-safe template for executing high-level reactive Pulsar operations.
|
||||
* A template for executing high-level reactive Pulsar operations.
|
||||
*
|
||||
* @param <T> the message payload type
|
||||
* @author Christophe Bornet
|
||||
@@ -47,9 +47,6 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
|
||||
private final SchemaResolver schemaResolver;
|
||||
|
||||
@Nullable
|
||||
private Schema<T> schema;
|
||||
|
||||
/**
|
||||
* Construct a template instance that uses the default schema resolver.
|
||||
* @param reactiveMessageSenderFactory the factory used to create the backing Pulsar
|
||||
@@ -76,8 +73,18 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MessageId> send(String topic, T message) {
|
||||
return doSend(topic, message, null, null);
|
||||
public Mono<MessageId> send(T message, @Nullable Schema<T> schema) {
|
||||
return doSend(null, message, schema, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MessageId> send(@Nullable String topic, T message) {
|
||||
return doSend(topic, message, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MessageId> send(@Nullable String topic, T message, @Nullable Schema<T> schema) {
|
||||
return doSend(topic, message, schema, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,8 +93,18 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<MessageId> send(String topic, Publisher<T> messages) {
|
||||
return doSendMany(topic, Flux.from(messages));
|
||||
public Flux<MessageId> send(Publisher<T> messages, @Nullable Schema<T> schema) {
|
||||
return doSendMany(null, Flux.from(messages), schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<MessageId> send(@Nullable String topic, Publisher<T> messages) {
|
||||
return doSendMany(topic, Flux.from(messages), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<MessageId> send(@Nullable String topic, Publisher<T> messages, @Nullable Schema<T> schema) {
|
||||
return doSendMany(topic, Flux.from(messages), schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,62 +112,64 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
return new SendMessageBuilderImpl<>(this, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the schema to use on this template.
|
||||
* @param schema provides the {@link Schema} used on this template
|
||||
*/
|
||||
public void setSchema(Schema<T> schema) {
|
||||
this.schema = schema;
|
||||
@Override
|
||||
public SendMessageBuilder<T> newMessages(Publisher<T> messages) {
|
||||
return new SendMessageBuilderImpl<>(this, messages);
|
||||
}
|
||||
|
||||
private Mono<MessageId> doSend(String topic, T message,
|
||||
MessageSpecBuilderCustomizer<T> messageSpecBuilderCustomizer,
|
||||
ReactiveMessageSenderBuilderCustomizer<T> customizer) {
|
||||
private Mono<MessageId> doSend(@Nullable String topic, T message, @Nullable Schema<T> schema,
|
||||
@Nullable MessageSpecBuilderCustomizer<T> messageSpecBuilderCustomizer,
|
||||
@Nullable ReactiveMessageSenderBuilderCustomizer<T> customizer) {
|
||||
String topicName = ReactiveMessageSenderUtils.resolveTopicName(topic, this.reactiveMessageSenderFactory);
|
||||
this.logger.trace(() -> String.format("Sending reactive message to '%s' topic", topicName));
|
||||
ReactiveMessageSender<T> sender = createMessageSender(topic, message, customizer);
|
||||
return sender.sendOne(getMessageSpec(messageSpecBuilderCustomizer, message)).doOnError(
|
||||
ex -> this.logger.error(ex, () -> String.format("Failed to send message to '%s' topic", topicName)))
|
||||
// NOTE: We do not pass the resolved topic name from above as it handles the
|
||||
// resolve itself
|
||||
ReactiveMessageSender<T> sender = createMessageSender(topic, message, schema, customizer);
|
||||
// @formatter:off
|
||||
return sender.sendOne(getMessageSpec(messageSpecBuilderCustomizer, message))
|
||||
.doOnError(ex -> this.logger.error(ex, () -> String.format("Failed to send message to '%s' topic", topicName)))
|
||||
.doOnSuccess(msgId -> this.logger.trace(() -> String.format("Sent message to '%s' topic", topicName)));
|
||||
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private Flux<MessageId> doSendMany(String topic, Flux<T> messages) {
|
||||
private Flux<MessageId> doSendMany(@Nullable String topic, Flux<T> messages, @Nullable Schema<T> schema) {
|
||||
String topicName = ReactiveMessageSenderUtils.resolveTopicName(topic, this.reactiveMessageSenderFactory);
|
||||
this.logger.trace(() -> String.format("Sending reactive messages to '%s' topic", topicName));
|
||||
|
||||
if (this.schema != null) {
|
||||
if (schema != null) {
|
||||
/*
|
||||
* If the template has a schema, we can create the message sender right away
|
||||
* and use ReactiveMessageSender::sendMany to send them as a stream.
|
||||
* Otherwise, we need to wait to get a message to create it and we can't share
|
||||
* it between messages. So we create one each time and use
|
||||
* If schema specified we can create the message sender right away and use
|
||||
* ReactiveMessageSender::sendMany to send them as a stream. Otherwise, we
|
||||
* need to wait to get a message to create the sender, and we can't share it
|
||||
* between messages. So we create one each time and use
|
||||
* ReactiveMessageSender::sendOne to send messages individually.
|
||||
*/
|
||||
ReactiveMessageSender<T> sender = createMessageSender(topic, null, null);
|
||||
// NOTE: We do not pass the resolved topic name from above as it handles the
|
||||
// resolve itself
|
||||
ReactiveMessageSender<T> sender = createMessageSender(topic, null, schema, null);
|
||||
return messages.map(MessageSpec::of).as(sender::sendMany)
|
||||
.doOnError(ex -> this.logger.error(ex,
|
||||
() -> String.format("Failed to send messages to '%s' topic", topicName)))
|
||||
.doOnNext(
|
||||
msgId -> this.logger.trace(() -> String.format("Sent messages to '%s' topic", topicName)));
|
||||
}
|
||||
return messages.flatMapSequential(message -> doSend(topic, message, null, null));
|
||||
return messages.flatMapSequential(message -> doSend(topic, message, schema, null, null));
|
||||
}
|
||||
|
||||
private static <T> MessageSpec<T> getMessageSpec(MessageSpecBuilderCustomizer<T> messageSpecBuilderCustomizer,
|
||||
T message) {
|
||||
private static <T> MessageSpec<T> getMessageSpec(
|
||||
@Nullable MessageSpecBuilderCustomizer<T> messageSpecBuilderCustomizer, T message) {
|
||||
MessageSpecBuilder<T> messageSpecBuilder = MessageSpec.builder(message);
|
||||
|
||||
if (messageSpecBuilderCustomizer != null) {
|
||||
messageSpecBuilderCustomizer.customize(messageSpecBuilder);
|
||||
}
|
||||
|
||||
return messageSpecBuilder.build();
|
||||
}
|
||||
|
||||
private ReactiveMessageSender<T> createMessageSender(String topic, T message,
|
||||
ReactiveMessageSenderBuilderCustomizer<T> customizer) {
|
||||
Schema<T> schema = this.schema != null ? this.schema : this.schemaResolver.getSchema(message);
|
||||
private ReactiveMessageSender<T> createMessageSender(@Nullable String topic, T message, @Nullable Schema<T> schema,
|
||||
@Nullable ReactiveMessageSenderBuilderCustomizer<T> customizer) {
|
||||
if (schema == null) {
|
||||
schema = this.schemaResolver.getSchema(message);
|
||||
}
|
||||
return this.reactiveMessageSenderFactory.createSender(topic, schema,
|
||||
customizer == null ? Collections.emptyList() : Collections.singletonList(customizer));
|
||||
}
|
||||
@@ -161,8 +180,12 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
|
||||
private final T message;
|
||||
|
||||
private final Publisher<T> messages;
|
||||
|
||||
private String topic;
|
||||
|
||||
private Schema<T> schema;
|
||||
|
||||
private MessageSpecBuilderCustomizer<T> messageCustomizer;
|
||||
|
||||
private ReactiveMessageSenderBuilderCustomizer<T> senderCustomizer;
|
||||
@@ -170,6 +193,13 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
SendMessageBuilderImpl(ReactivePulsarTemplate<T> template, T message) {
|
||||
this.template = template;
|
||||
this.message = message;
|
||||
this.messages = null;
|
||||
}
|
||||
|
||||
SendMessageBuilderImpl(ReactivePulsarTemplate<T> template, Publisher<T> messages) {
|
||||
this.template = template;
|
||||
this.message = null;
|
||||
this.messages = messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,6 +208,12 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendMessageBuilderImpl<T> withSchema(Schema<T> schema) {
|
||||
this.schema = schema;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendMessageBuilderImpl<T> withMessageCustomizer(MessageSpecBuilderCustomizer<T> messageCustomizer) {
|
||||
this.messageCustomizer = messageCustomizer;
|
||||
@@ -193,7 +229,13 @@ public class ReactivePulsarTemplate<T> implements ReactivePulsarOperations<T> {
|
||||
|
||||
@Override
|
||||
public Mono<MessageId> send() {
|
||||
return this.template.doSend(this.topic, this.message, this.messageCustomizer, this.senderCustomizer);
|
||||
return this.template.doSend(this.topic, this.message, this.schema, this.messageCustomizer,
|
||||
this.senderCustomizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<MessageId> sendMany() {
|
||||
return this.template.doSendMany(this.topic, Flux.from(this.messages), this.schema);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ import org.apache.pulsar.client.api.PulsarClient;
|
||||
import org.apache.pulsar.client.api.Schema;
|
||||
import org.apache.pulsar.reactive.client.api.MutableReactiveMessageSenderSpec;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.pulsar.core.DefaultSchemaResolver;
|
||||
import org.springframework.pulsar.core.PulsarTestContainerSupport;
|
||||
@@ -50,29 +50,37 @@ import reactor.core.publisher.Mono;
|
||||
* Tests for {@link org.springframework.pulsar.reactive.core.ReactivePulsarTemplate}.
|
||||
*
|
||||
* @author Christophe Bornet
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class ReactivePulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
|
||||
@Test
|
||||
void sendMessagesWithSpecificSchema() throws Exception {
|
||||
String topic = "smt-specific-schema-reactive-topic";
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { true, false })
|
||||
void sendMessagesWithSpecificSchema(boolean useSimpleApi) throws Exception {
|
||||
String topic = "rptt-sendMessagesWithSpecificSchema-" + useSimpleApi + "-topic";
|
||||
String sub = "rptt-sendMessagesWithSpecificSchema-" + useSimpleApi + "-sub";
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
try (Consumer<Foo> consumer = client.newConsumer(Schema.JSON(Foo.class)).topic(topic)
|
||||
.subscriptionName("smt-specific-schema-reactive-sub").subscribe()) {
|
||||
try (Consumer<Foo> consumer = client.newConsumer(Schema.JSON(Foo.class)).topic(topic).subscriptionName(sub)
|
||||
.subscribe()) {
|
||||
MutableReactiveMessageSenderSpec senderSpec = new MutableReactiveMessageSenderSpec();
|
||||
senderSpec.setTopicName(topic);
|
||||
org.springframework.pulsar.reactive.core.ReactivePulsarSenderFactory<Foo> producerFactory = new org.springframework.pulsar.reactive.core.DefaultReactivePulsarSenderFactory<>(
|
||||
client, senderSpec, null);
|
||||
org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<Foo> pulsarTemplate = new org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<>(
|
||||
producerFactory);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
ReactivePulsarSenderFactory<Foo> producerFactory = new DefaultReactivePulsarSenderFactory<>(client,
|
||||
senderSpec, null);
|
||||
ReactivePulsarTemplate<Foo> pulsarTemplate = new ReactivePulsarTemplate<>(producerFactory);
|
||||
|
||||
List<Foo> foos = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
foos.add(new Foo("Foo-" + UUID.randomUUID(), "Bar-" + UUID.randomUUID()));
|
||||
}
|
||||
pulsarTemplate.send(Flux.fromIterable(foos)).subscribe();
|
||||
|
||||
if (useSimpleApi) {
|
||||
pulsarTemplate.send(Flux.fromIterable(foos), Schema.JSON(Foo.class)).subscribe();
|
||||
}
|
||||
else {
|
||||
pulsarTemplate.newMessages(Flux.fromIterable(foos)).withSchema(Schema.JSON(Foo.class)).sendMany()
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertThat(consumer.receiveAsync()).succeedsWithin(Duration.ofSeconds(3))
|
||||
@@ -82,31 +90,39 @@ class ReactivePulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendMessagesWithSpecificSchemaAndCustomTypeMappings() throws Exception {
|
||||
String topic = "smt-specific-schema-custom-reactive-topic";
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { true, false })
|
||||
void sendMessagesWithInferredSchema(boolean useSimpleApi) throws Exception {
|
||||
String topic = "rptt-sendMessagesWithInferredSchema-" + useSimpleApi + "-topic";
|
||||
String sub = "rptt-sendMessagesWithInferredSchema-" + useSimpleApi + "-sub";
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
try (Consumer<Foo> consumer = client.newConsumer(Schema.JSON(Foo.class)).topic(topic)
|
||||
.subscriptionName("smt-specific-schema-custom-reactive-sub").subscribe()) {
|
||||
try (Consumer<Foo> consumer = client.newConsumer(Schema.JSON(Foo.class)).topic(topic).subscriptionName(sub)
|
||||
.subscribe()) {
|
||||
MutableReactiveMessageSenderSpec senderSpec = new MutableReactiveMessageSenderSpec();
|
||||
senderSpec.setTopicName(topic);
|
||||
org.springframework.pulsar.reactive.core.ReactivePulsarSenderFactory<Foo> producerFactory = new org.springframework.pulsar.reactive.core.DefaultReactivePulsarSenderFactory<>(
|
||||
client, senderSpec, null);
|
||||
// Custom schema resolver allows not calling setSchema on template
|
||||
ReactivePulsarSenderFactory<Foo> producerFactory = new DefaultReactivePulsarSenderFactory<>(client,
|
||||
senderSpec, null);
|
||||
// Custom schema resolver allows not specifying the schema when sending
|
||||
DefaultSchemaResolver schemaResolver = new DefaultSchemaResolver();
|
||||
schemaResolver.addCustomSchemaMapping(Foo.class, Schema.JSON(Foo.class));
|
||||
org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<Foo> pulsarTemplate = new org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<>(
|
||||
producerFactory, schemaResolver);
|
||||
ReactivePulsarTemplate<Foo> pulsarTemplate = new ReactivePulsarTemplate<>(producerFactory,
|
||||
schemaResolver);
|
||||
|
||||
List<Foo> foos = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
foos.add(new Foo("Foo-" + UUID.randomUUID(), "Bar-" + UUID.randomUUID()));
|
||||
}
|
||||
pulsarTemplate.send(Flux.fromIterable(foos)).subscribe();
|
||||
|
||||
// TODO figure out why ordering is not preserved when template does not
|
||||
// have schema set
|
||||
if (useSimpleApi) {
|
||||
pulsarTemplate.send(Flux.fromIterable(foos)).subscribe();
|
||||
}
|
||||
else {
|
||||
pulsarTemplate.newMessages(Flux.fromIterable(foos)).sendMany().subscribe();
|
||||
}
|
||||
|
||||
// TODO figure out if expected to not be ordered when schema not set on
|
||||
// template
|
||||
List<Foo> foos2 = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
CompletableFuture<Message<Foo>> receiveFuture = consumer.receiveAsync();
|
||||
@@ -126,39 +142,49 @@ class ReactivePulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
String subscription = topic + "-sub";
|
||||
String msgPayload = topic + "-msg";
|
||||
MessageSpecBuilderCustomizer<String> messageCustomizer = null;
|
||||
if (testArgs.useMessageCustomizer) {
|
||||
if (testArgs.messageCustomizer) {
|
||||
messageCustomizer = (mb) -> mb.key("foo-key");
|
||||
}
|
||||
ReactiveMessageSenderBuilderCustomizer<String> senderCustomizer = null;
|
||||
if (testArgs.useSenderCustomizer) {
|
||||
senderCustomizer = (sb) -> sb.producerName("foo-producer");
|
||||
if (testArgs.senderCustomizer) {
|
||||
senderCustomizer = (sb) -> sb.producerName("foo-sender");
|
||||
}
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
try (Consumer<String> consumer = client.newConsumer(Schema.STRING).topic(topic)
|
||||
.subscriptionName(subscription).subscribe()) {
|
||||
|
||||
MutableReactiveMessageSenderSpec senderSpec = new MutableReactiveMessageSenderSpec();
|
||||
if (!testArgs.useSpecificTopic) {
|
||||
if (!testArgs.explicitTopic) {
|
||||
senderSpec.setTopicName(topic);
|
||||
}
|
||||
ReactivePulsarSenderFactory<String> senderFactory = new DefaultReactivePulsarSenderFactory<>(client,
|
||||
senderSpec, null);
|
||||
org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<String> pulsarTemplate = new org.springframework.pulsar.reactive.core.ReactivePulsarTemplate<>(
|
||||
senderFactory);
|
||||
ReactivePulsarTemplate<String> pulsarTemplate = new ReactivePulsarTemplate<>(senderFactory);
|
||||
Mono<MessageId> sendResponse;
|
||||
if (testArgs.useTemplateSchema) {
|
||||
pulsarTemplate.setSchema(Schema.STRING);
|
||||
}
|
||||
if (testArgs.useSimpleApi) {
|
||||
sendResponse = testArgs.useSpecificTopic ? pulsarTemplate.send(topic, msgPayload)
|
||||
: pulsarTemplate.send(msgPayload);
|
||||
if (testArgs.simpleApi) {
|
||||
if (testArgs.explicitSchema && testArgs.explicitTopic) {
|
||||
sendResponse = pulsarTemplate.send(topic, msgPayload, Schema.STRING);
|
||||
}
|
||||
else if (testArgs.explicitSchema) {
|
||||
sendResponse = pulsarTemplate.send(msgPayload, Schema.STRING);
|
||||
}
|
||||
else if (testArgs.explicitTopic) {
|
||||
sendResponse = pulsarTemplate.send(topic, msgPayload);
|
||||
}
|
||||
else {
|
||||
sendResponse = pulsarTemplate.send(msgPayload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ReactivePulsarTemplate.SendMessageBuilderImpl<String> messageBuilder = pulsarTemplate
|
||||
.newMessage(msgPayload);
|
||||
if (testArgs.useSpecificTopic) {
|
||||
if (testArgs.explicitTopic) {
|
||||
messageBuilder = messageBuilder.withTopic(topic);
|
||||
}
|
||||
if (testArgs.explicitSchema) {
|
||||
messageBuilder = messageBuilder.withSchema(Schema.STRING);
|
||||
}
|
||||
if (messageCustomizer != null) {
|
||||
messageBuilder = messageBuilder.withMessageCustomizer(messageCustomizer);
|
||||
}
|
||||
@@ -177,7 +203,7 @@ class ReactivePulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
assertThat(msg.getKey()).isEqualTo("foo-key");
|
||||
}
|
||||
if (senderCustomizer != null) {
|
||||
assertThat(msg.getProducerName()).isEqualTo("foo-producer");
|
||||
assertThat(msg.getProducerName()).isEqualTo("foo-sender");
|
||||
}
|
||||
// Make sure the producer was closed by the template (albeit indirectly as
|
||||
// client removes closed producers)
|
||||
@@ -188,67 +214,61 @@ class ReactivePulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
|
||||
private static Stream<Arguments> sendMessageTestProvider() {
|
||||
return Stream.of(arguments("sendReactiveMessageToDefaultTopic", SendTestArgs.useSpecificTopic(false)),
|
||||
arguments("sendReactiveMessageToDefaultTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(false).useSimpleApi()),
|
||||
arguments("sendReactiveMessageToDefaultTopicWithSimpleApiAndTemplateSchema",
|
||||
SendTestArgs.useSpecificTopic(false).useSimpleApi().useTemplateSchema()),
|
||||
arguments("sendReactiveMessageToDefaultTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer()),
|
||||
arguments("sendReactiveMessageToDefaultTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useSenderCustomizer()),
|
||||
arguments("sendReactiveMessageToDefaultTopicWithAllOptions",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer().useSenderCustomizer()),
|
||||
arguments("sendReactiveMessageToSpecificTopic", SendTestArgs.useSpecificTopic(true)),
|
||||
arguments("sendReactiveMessageToSpecificTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(true).useSimpleApi()),
|
||||
arguments("sendReactiveMessageToSpecificTopicWithSimpleApiAndTemplateSchema",
|
||||
SendTestArgs.useSpecificTopic(true).useSimpleApi().useTemplateSchema()),
|
||||
arguments("sendReactiveMessageToSpecificTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useMessageCustomizer()),
|
||||
arguments("sendReactiveMessageToSpecificTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useSenderCustomizer()),
|
||||
arguments("sendReactiveMessageToSpecificTopicWithAllOptions",
|
||||
SendTestArgs.useSpecificTopic(true).useMessageCustomizer().useSenderCustomizer()));
|
||||
return Stream.of(arguments("simpleReactiveSend", SendTestArgs.simple()),
|
||||
arguments("simpleReactiveSendWithTopic", SendTestArgs.simple().topic()),
|
||||
arguments("simpleReactiveSendWithSchema", SendTestArgs.simple().schema()),
|
||||
arguments("simpleReactiveSendWithTopicAndSchema", SendTestArgs.simple().topic().schema()),
|
||||
arguments("fluentReactiveSend", SendTestArgs.fluent()),
|
||||
arguments("fluentReactiveSendWithSchema", SendTestArgs.fluent().schema()),
|
||||
arguments("fluentReactiveSendWithTopic", SendTestArgs.fluent().topic()),
|
||||
arguments("fluentReactiveSendWithMessageCustomizer", SendTestArgs.fluent().messageCustomizer()),
|
||||
arguments("fluentReactiveSendWithSenderCustomizer", SendTestArgs.fluent().senderCustomizer()),
|
||||
arguments("fluentReactiveSendWithTopicAndSchema", SendTestArgs.fluent().topic().schema()),
|
||||
arguments("fluentReactiveSendWithTopicAndSchemaAndCustomizers",
|
||||
SendTestArgs.fluent().topic().schema().messageCustomizer().senderCustomizer()));
|
||||
}
|
||||
|
||||
static final class SendTestArgs {
|
||||
|
||||
private final boolean useSpecificTopic;
|
||||
private boolean simpleApi;
|
||||
|
||||
private boolean useMessageCustomizer;
|
||||
private boolean explicitTopic;
|
||||
|
||||
private boolean useSenderCustomizer;
|
||||
private boolean explicitSchema;
|
||||
|
||||
private boolean useSimpleApi;
|
||||
private boolean messageCustomizer;
|
||||
|
||||
private boolean useTemplateSchema;
|
||||
private boolean senderCustomizer;
|
||||
|
||||
private SendTestArgs(boolean useSpecificTopic) {
|
||||
this.useSpecificTopic = useSpecificTopic;
|
||||
private SendTestArgs(boolean simpleApi) {
|
||||
this.simpleApi = simpleApi;
|
||||
}
|
||||
|
||||
static SendTestArgs useSpecificTopic(boolean useSpecificTopic) {
|
||||
return new SendTestArgs(useSpecificTopic);
|
||||
static SendTestArgs simple() {
|
||||
return new SendTestArgs(true);
|
||||
}
|
||||
|
||||
SendTestArgs useMessageCustomizer() {
|
||||
this.useMessageCustomizer = true;
|
||||
static SendTestArgs fluent() {
|
||||
return new SendTestArgs(false);
|
||||
}
|
||||
|
||||
SendTestArgs topic() {
|
||||
this.explicitTopic = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useSenderCustomizer() {
|
||||
this.useSenderCustomizer = true;
|
||||
SendTestArgs schema() {
|
||||
this.explicitSchema = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useSimpleApi() {
|
||||
this.useSimpleApi = true;
|
||||
SendTestArgs messageCustomizer() {
|
||||
this.messageCustomizer = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useTemplateSchema() {
|
||||
this.useTemplateSchema = true;
|
||||
SendTestArgs senderCustomizer() {
|
||||
this.senderCustomizer = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ import reactor.core.publisher.Mono;
|
||||
* Tests for {@link ReactivePulsarListener} annotation.
|
||||
*
|
||||
* @author Christophe Bornet
|
||||
* @author Chris Bono
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
@@ -199,13 +200,13 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(id = "id-1", topics = "topic-1", subscriptionName = "subscription-1",
|
||||
consumerCustomizer = "consumerCustomizer")
|
||||
Mono<Void> listen1(String message) {
|
||||
Mono<Void> listen1(String ignored) {
|
||||
latch1.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(consumerCustomizer = "listen2Customizer")
|
||||
Mono<Void> listen2(String message) {
|
||||
Mono<Void> listen2(String ignored) {
|
||||
latch2.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -218,7 +219,7 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(id = "id-3", topicPattern = "persistent://public/default/pattern.*",
|
||||
subscriptionName = "subscription-3", consumerCustomizer = "consumerCustomizer")
|
||||
Mono<Void> listen3(String message) {
|
||||
Mono<Void> listen3(String ignored) {
|
||||
latch3.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -350,10 +351,8 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(JSONSchema.of(User.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("json-topic", new User("Jason", i));
|
||||
template.send("json-topic", new User("Jason", i), JSONSchema.of(User.class));
|
||||
}
|
||||
assertThat(jsonLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -363,10 +362,8 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(AvroSchema.of(User.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("avro-topic", new User("Avi", i));
|
||||
template.send("avro-topic", new User("Avi", i), AvroSchema.of(User.class));
|
||||
}
|
||||
assertThat(avroLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -378,10 +375,8 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarTemplate<KeyValue<String, Integer>> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
Schema<KeyValue<String, Integer>> kvSchema = Schema.KeyValue(Schema.STRING, Schema.INT32,
|
||||
KeyValueEncodingType.INLINE);
|
||||
template.setSchema(kvSchema);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("keyvalue-topic", new KeyValue<>("Kevin", i));
|
||||
template.send("keyvalue-topic", new KeyValue<>("Kevin", i), kvSchema);
|
||||
}
|
||||
assertThat(keyvalueLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -391,10 +386,9 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<Proto.Person> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<Proto.Person> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(ProtobufSchema.of(Proto.Person.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("protobuf-topic", Proto.Person.newBuilder().setId(i).setName("Paul").build());
|
||||
template.send("protobuf-topic", Proto.Person.newBuilder().setId(i).setName("Paul").build(),
|
||||
ProtobufSchema.of(Proto.Person.class));
|
||||
}
|
||||
assertThat(protobufLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -405,28 +399,28 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(id = "jsonListener", topics = "json-topic", schemaType = SchemaType.JSON,
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenJson(User message) {
|
||||
Mono<Void> listenJson(User ignored) {
|
||||
jsonLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "avroListener", topics = "avro-topic", schemaType = SchemaType.AVRO,
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenAvro(User message) {
|
||||
Mono<Void> listenAvro(User ignored) {
|
||||
avroLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "keyvalueListener", topics = "keyvalue-topic",
|
||||
schemaType = SchemaType.KEY_VALUE, consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenKeyvalue(KeyValue<String, Integer> message) {
|
||||
Mono<Void> listenKeyvalue(KeyValue<String, Integer> ignored) {
|
||||
keyvalueLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "protobufListener", topics = "protobuf-topic",
|
||||
schemaType = SchemaType.PROTOBUF, consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenProtobuf(Proto.Person message) {
|
||||
Mono<Void> listenProtobuf(Proto.Person ignored) {
|
||||
protobufLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -509,10 +503,8 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User2> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User2> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(JSONSchema.of(User2.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("json-custom-schema-topic", new User2("Jason", i));
|
||||
template.send("json-custom-schema-topic", new User2("Jason", i), JSONSchema.of(User2.class));
|
||||
}
|
||||
assertThat(jsonLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -522,10 +514,8 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(AvroSchema.of(User.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("avro-custom-schema-topic", new User("Avi", i));
|
||||
template.send("avro-custom-schema-topic", new User("Avi", i), AvroSchema.of(User.class));
|
||||
}
|
||||
assertThat(avroLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -537,10 +527,9 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarTemplate<KeyValue<String, User2>> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
Schema<KeyValue<String, User2>> kvSchema = Schema.KeyValue(Schema.STRING, Schema.JSON(User2.class),
|
||||
KeyValueEncodingType.INLINE);
|
||||
template.setSchema(kvSchema);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("keyvalue-custom-schema-topic", new KeyValue<>("Kevin", new User2("Kevin", 5150)));
|
||||
template.send("keyvalue-custom-schema-topic", new KeyValue<>("Kevin", new User2("Kevin", 5150)),
|
||||
kvSchema);
|
||||
}
|
||||
assertThat(keyvalueLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -550,11 +539,10 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<Proto.Person> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<Proto.Person> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(ProtobufSchema.of(Proto.Person.class));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("protobuf-custom-schema-topic",
|
||||
Proto.Person.newBuilder().setId(i).setName("Paul").build());
|
||||
Proto.Person.newBuilder().setId(i).setName("Paul").build(),
|
||||
ProtobufSchema.of(Proto.Person.class));
|
||||
}
|
||||
assertThat(protobufLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -582,28 +570,28 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(id = "jsonListener", topics = "json-custom-schema-topic",
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenJson(User2 message) {
|
||||
Mono<Void> listenJson(User2 ignored) {
|
||||
jsonLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "avroListener", topics = "avro-custom-schema-topic",
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenAvro(User message) {
|
||||
Mono<Void> listenAvro(User ignored) {
|
||||
avroLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "keyvalueListener", topics = "keyvalue-custom-schema-topic",
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenKeyvalue(KeyValue<String, User2> message) {
|
||||
Mono<Void> listenKeyvalue(KeyValue<String, User2> ignored) {
|
||||
keyvalueLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(id = "protobufListener", topics = "protobuf-custom-schema-topic",
|
||||
consumerCustomizer = "subscriptionInitialPositionEarliest")
|
||||
Mono<Void> listenProtobuf(Proto.Person message) {
|
||||
Mono<Void> listenProtobuf(Proto.Person ignored) {
|
||||
protobufLatch.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -827,7 +815,7 @@ public class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(topics = "pulsarListenerConcurrency", consumerCustomizer = "consumerCustomizer",
|
||||
concurrency = "100")
|
||||
Mono<Void> listen1(String message) {
|
||||
Mono<Void> listen1(String ignored) {
|
||||
latch.countDown();
|
||||
// if messages are not handled concurrently, this will make the latch
|
||||
// await timeout.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -93,11 +93,10 @@ public class SpringPulsarBootApp {
|
||||
|
||||
String topic = "hello-pulsar-exclusive-3";
|
||||
PulsarTemplate<Foo> pulsarTemplate = new PulsarTemplate<>(producerFactory);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
return args -> {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Foo foo = new Foo(i + "-" + "Foo-" + UUID.randomUUID(), i + "-" + "Bar-" + UUID.randomUUID());
|
||||
pulsarTemplate.send(topic, foo);
|
||||
pulsarTemplate.send(topic, foo, Schema.JSON(Foo.class));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -139,11 +138,10 @@ public class SpringPulsarBootApp {
|
||||
|
||||
String topic = "hello-pulsar-exclusive-5";
|
||||
PulsarTemplate<Foo> pulsarTemplate = new PulsarTemplate<>(producerFactory);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
return args -> {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Foo foo = new Foo(i + "-" + "Foo-" + UUID.randomUUID(), i + "-" + "Bar-" + UUID.randomUUID());
|
||||
pulsarTemplate.send(topic, foo);
|
||||
pulsarTemplate.send(topic, foo, Schema.JSON(Foo.class));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -88,9 +88,10 @@ public class ReactiveSpringPulsarBootApp {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||
this.reactivePulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
Schema<Foo> schema = Schema.JSON(Foo.class);
|
||||
Flux.range(0, 10).map((i) -> new Foo("Foo-" + i, "Bar-" + i))
|
||||
.as(messages -> this.reactivePulsarTemplate.send("sample-reactive-topic2", messages)).subscribe();
|
||||
.as(messages -> this.reactivePulsarTemplate.send("sample-reactive-topic2", messages, schema))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@ReactivePulsarListener(subscriptionName = "sample-reactive-sub2", topics = "sample-reactive-topic2",
|
||||
|
||||
@@ -77,8 +77,7 @@ class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
.run("--spring.pulsar.client.serviceUrl=" + PulsarTestContainerSupport.getPulsarBrokerUrl())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
PulsarTemplate<Foo> pulsarTemplate = context.getBean(PulsarTemplate.class);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
pulsarTemplate.send("plt-custom-topic1", new Foo("John Doe"));
|
||||
pulsarTemplate.send("plt-custom-topic1", new Foo("John Doe"), Schema.JSON(Foo.class));
|
||||
assertThat(LATCH_2.await(20, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -118,7 +117,7 @@ class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
static class BasicListenerConfig {
|
||||
|
||||
@PulsarListener(subscriptionName = "plt-sub1", topics = "plt-topic1")
|
||||
public void listen(String foo) {
|
||||
public void listen(String ignored) {
|
||||
LATCH_1.countDown();
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(subscriptionName = "plt-custom-sub1", topics = "plt-custom-topic1",
|
||||
schemaType = SchemaType.JSON)
|
||||
public void listen(Foo foo) {
|
||||
public void listen(Foo ignored) {
|
||||
LATCH_2.countDown();
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "plt-custom-sub2", topics = "plt-custom-topic2")
|
||||
public void listen(Foo foo) {
|
||||
public void listen(Foo ignored) {
|
||||
LATCH_3.countDown();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,7 @@ class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
.run("--spring.pulsar.client.serviceUrl=" + PulsarTestContainerSupport.getPulsarBrokerUrl())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ReactivePulsarTemplate<Foo> pulsarTemplate = context.getBean(ReactivePulsarTemplate.class);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
pulsarTemplate.send("rplt-custom-topic1", new Foo("John Doe")).block();
|
||||
pulsarTemplate.send("rplt-custom-topic1", new Foo("John Doe"), Schema.JSON(Foo.class)).block();
|
||||
assertThat(LATCH2.await(20, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -129,7 +128,7 @@ class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(subscriptionName = "rplt-sub1", topics = "rplt-topic1",
|
||||
consumerCustomizer = "consumerCustomizer")
|
||||
public Mono<Void> listen(String foo) {
|
||||
public Mono<Void> listen(String ignored) {
|
||||
LATCH1.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -143,7 +142,7 @@ class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(subscriptionName = "rplt-custom-sub1", topics = "rplt-custom-topic1",
|
||||
schemaType = SchemaType.JSON, consumerCustomizer = "consumerCustomizer")
|
||||
public Mono<Void> listen(Foo foo) {
|
||||
public Mono<Void> listen(Foo ignored) {
|
||||
LATCH2.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -164,7 +163,7 @@ class ReactivePulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ReactivePulsarListener(subscriptionName = "rplt-custom-sub2", topics = "rplt-custom-topic2",
|
||||
consumerCustomizer = "consumerCustomizer")
|
||||
public Mono<Void> listen(Foo foo) {
|
||||
public Mono<Void> listen(Foo ignored) {
|
||||
LATCH3.countDown();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.pulsar.spring.cloud.stream.binder.provisioning.Pulsar
|
||||
* {@link Binder} implementation for Apache Pulsar.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class PulsarMessageChannelBinder extends
|
||||
AbstractMessageChannelBinder<ExtendedConsumerProperties<PulsarConsumerProperties>, ExtendedProducerProperties<PulsarProducerProperties>, PulsarTopicProvisioner>
|
||||
@@ -80,21 +81,24 @@ public class PulsarMessageChannelBinder extends
|
||||
@Override
|
||||
protected MessageHandler createProducerMessageHandler(ProducerDestination destination,
|
||||
ExtendedProducerProperties<PulsarProducerProperties> producerProperties, MessageChannel errorChannel) {
|
||||
|
||||
final Schema<Object> schema;
|
||||
if (producerProperties.isUseNativeEncoding()) {
|
||||
Schema<Object> schema = resolveSchema(producerProperties.getExtension().getSchemaType(),
|
||||
schema = resolveSchema(producerProperties.getExtension().getSchemaType(),
|
||||
producerProperties.getExtension().getMessageType(),
|
||||
producerProperties.getExtension().getMessageKeyType(),
|
||||
producerProperties.getExtension().getMessageValueType());
|
||||
this.pulsarTemplate.setSchema(
|
||||
Objects.requireNonNull(schema, "Could not determine producer schema for " + destination.getName()));
|
||||
Objects.requireNonNull(schema, "Could not determine producer schema for " + destination.getName());
|
||||
}
|
||||
else {
|
||||
schema = null;
|
||||
}
|
||||
return message -> {
|
||||
try {
|
||||
PulsarMessageChannelBinder.this.pulsarTemplate.sendAsync(destination.getName(), message.getPayload());
|
||||
PulsarMessageChannelBinder.this.pulsarTemplate.sendAsync(destination.getName(), message.getPayload(),
|
||||
schema);
|
||||
}
|
||||
catch (PulsarClientException e) {
|
||||
// deal later
|
||||
catch (PulsarClientException ex) {
|
||||
this.logger.trace("Failed to send message to destination: " + destination.getName(), ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -109,7 +113,6 @@ public class PulsarMessageChannelBinder extends
|
||||
Message<Object> message = MessageBuilder.withPayload(msg.getValue()).build();
|
||||
pulsarMessageDrivenChannelAdapter.send(message);
|
||||
});
|
||||
|
||||
if (properties.isUseNativeDecoding()) {
|
||||
Schema<Object> schema = resolveSchema(properties.getExtension().getSchemaType(),
|
||||
properties.getExtension().getMessageType(), properties.getExtension().getMessageKeyType(),
|
||||
@@ -120,7 +123,6 @@ public class PulsarMessageChannelBinder extends
|
||||
else {
|
||||
pulsarContainerProperties.setSchema(Schema.BYTES);
|
||||
}
|
||||
|
||||
String subscriptionName = PulsarBinderUtils.subscriptionName(properties.getExtension(), destination);
|
||||
pulsarContainerProperties.setSubscriptionName(subscriptionName);
|
||||
DefaultPulsarMessageListenerContainer<?> container = new DefaultPulsarMessageListenerContainer<>(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.apache.pulsar.client.api.MessageId;
|
||||
import org.apache.pulsar.client.api.PulsarClientException;
|
||||
import org.apache.pulsar.client.api.Schema;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -41,6 +42,16 @@ public interface PulsarOperations<T> {
|
||||
*/
|
||||
MessageId send(T message) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the default topic in a blocking manner.
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to send using the default schema
|
||||
* resolution
|
||||
* @return the id assigned by the broker to the published message
|
||||
* @throws PulsarClientException if an error occurs
|
||||
*/
|
||||
MessageId send(T message, @Nullable Schema<T> schema) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a blocking manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
@@ -51,6 +62,18 @@ public interface PulsarOperations<T> {
|
||||
*/
|
||||
MessageId send(@Nullable String topic, T message) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a blocking manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
* default topic
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to send using the default schema
|
||||
* resolution
|
||||
* @return the id assigned by the broker to the published message
|
||||
* @throws PulsarClientException if an error occurs
|
||||
*/
|
||||
MessageId send(@Nullable String topic, T message, @Nullable Schema<T> schema) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the default topic in a non-blocking manner.
|
||||
* @param message the message to send
|
||||
@@ -59,6 +82,16 @@ public interface PulsarOperations<T> {
|
||||
*/
|
||||
CompletableFuture<MessageId> sendAsync(T message) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the default topic in a non-blocking manner.
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to send using the default schema
|
||||
* resolution
|
||||
* @return a future that holds the id assigned by the broker to the published message
|
||||
* @throws PulsarClientException if an error occurs
|
||||
*/
|
||||
CompletableFuture<MessageId> sendAsync(T message, @Nullable Schema<T> schema) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a non-blocking manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
@@ -69,6 +102,19 @@ public interface PulsarOperations<T> {
|
||||
*/
|
||||
CompletableFuture<MessageId> sendAsync(@Nullable String topic, T message) throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Sends a message to the specified topic in a non-blocking manner.
|
||||
* @param topic the topic to send the message to or {@code null} to send to the
|
||||
* default topic
|
||||
* @param message the message to send
|
||||
* @param schema the schema to use or {@code null} to send using the default schema
|
||||
* resolution
|
||||
* @return a future that holds the id assigned by the broker to the published message
|
||||
* @throws PulsarClientException if an error occurs
|
||||
*/
|
||||
CompletableFuture<MessageId> sendAsync(@Nullable String topic, T message, @Nullable Schema<T> schema)
|
||||
throws PulsarClientException;
|
||||
|
||||
/**
|
||||
* Create a {@link SendMessageBuilder builder} for configuring and sending a message.
|
||||
* @param message the payload of the message
|
||||
@@ -91,6 +137,13 @@ public interface PulsarOperations<T> {
|
||||
*/
|
||||
SendMessageBuilder<T> withTopic(String topic);
|
||||
|
||||
/**
|
||||
* Specify the schema to use when sending the message.
|
||||
* @param schema the schema to use
|
||||
* @return the current builder with the schema specified
|
||||
*/
|
||||
SendMessageBuilder<T> withSchema(Schema<T> schema);
|
||||
|
||||
/**
|
||||
* Specify the encryption keys to use.
|
||||
* @param encryptionKeys the encryption keys
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.apache.pulsar.client.api.MessageId;
|
||||
@@ -42,7 +43,7 @@ import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
/**
|
||||
* A thread-safe template for executing high-level Pulsar operations.
|
||||
* A template for executing high-level Pulsar operations.
|
||||
*
|
||||
* @param <T> the message payload type
|
||||
* @author Soby Chacko
|
||||
@@ -56,7 +57,6 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
|
||||
private final PulsarProducerFactory<T> producerFactory;
|
||||
|
||||
@Nullable
|
||||
private final List<ProducerInterceptor> interceptors;
|
||||
|
||||
private final SchemaResolver schemaResolver;
|
||||
@@ -69,9 +69,6 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
|
||||
private String beanName = "";
|
||||
|
||||
@Nullable
|
||||
private Schema<T> schema;
|
||||
|
||||
/**
|
||||
* Construct a template instance without interceptors that uses the default schema
|
||||
* resolver.
|
||||
@@ -113,22 +110,43 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
|
||||
@Override
|
||||
public MessageId send(T message) throws PulsarClientException {
|
||||
return doSend(null, null, message, null, null);
|
||||
return doSend(null, message, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageId send(T message, @Nullable Schema<T> schema) throws PulsarClientException {
|
||||
return doSend(null, message, schema, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageId send(@Nullable String topic, T message) throws PulsarClientException {
|
||||
return doSend(topic, null, message, null, null);
|
||||
return doSend(topic, message, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageId send(@Nullable String topic, T message, @Nullable Schema<T> schema) throws PulsarClientException {
|
||||
return doSend(topic, message, schema, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageId> sendAsync(T message) throws PulsarClientException {
|
||||
return doSendAsync(null, null, message, null, null);
|
||||
return doSendAsync(null, message, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageId> sendAsync(T message, @Nullable Schema<T> schema) throws PulsarClientException {
|
||||
return doSendAsync(null, message, schema, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageId> sendAsync(@Nullable String topic, T message) throws PulsarClientException {
|
||||
return doSendAsync(topic, null, message, null, null);
|
||||
return doSendAsync(topic, message, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageId> sendAsync(@Nullable String topic, T message, @Nullable Schema<T> schema)
|
||||
throws PulsarClientException {
|
||||
return doSendAsync(topic, message, schema, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,27 +159,21 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the schema to use on this template.
|
||||
* @param schema provides the {@link Schema} used on this template
|
||||
*/
|
||||
public void setSchema(Schema<T> schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
private MessageId doSend(@Nullable String topic, @Nullable Collection<String> encryptionKeys, T message,
|
||||
private MessageId doSend(@Nullable String topic, T message, @Nullable Schema<T> schema,
|
||||
@Nullable Collection<String> encryptionKeys,
|
||||
@Nullable TypedMessageBuilderCustomizer<T> typedMessageBuilderCustomizer,
|
||||
@Nullable ProducerBuilderCustomizer<T> producerCustomizer) throws PulsarClientException {
|
||||
try {
|
||||
return doSendAsync(topic, encryptionKeys, message, typedMessageBuilderCustomizer, producerCustomizer).get();
|
||||
return doSendAsync(topic, message, schema, encryptionKeys, typedMessageBuilderCustomizer,
|
||||
producerCustomizer).get();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw PulsarClientException.unwrap(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<MessageId> doSendAsync(@Nullable String topic,
|
||||
@Nullable Collection<String> encryptionKeys, T message,
|
||||
private CompletableFuture<MessageId> doSendAsync(@Nullable String topic, T message, @Nullable Schema<T> schema,
|
||||
@Nullable Collection<String> encryptionKeys,
|
||||
@Nullable TypedMessageBuilderCustomizer<T> typedMessageBuilderCustomizer,
|
||||
@Nullable ProducerBuilderCustomizer<T> producerCustomizer) throws PulsarClientException {
|
||||
String topicName = ProducerUtils.resolveTopicName(topic, this.producerFactory);
|
||||
@@ -171,7 +183,7 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
Observation observation = newObservation(senderContext);
|
||||
try {
|
||||
observation.start();
|
||||
Producer<T> producer = prepareProducerForSend(topic, message, encryptionKeys, producerCustomizer);
|
||||
Producer<T> producer = prepareProducerForSend(topic, message, schema, encryptionKeys, producerCustomizer);
|
||||
TypedMessageBuilder<T> messageBuilder = producer.newMessage().value(message);
|
||||
if (typedMessageBuilderCustomizer != null) {
|
||||
typedMessageBuilderCustomizer.customize(messageBuilder);
|
||||
@@ -207,10 +219,13 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
DefaultPulsarTemplateObservationConvention.INSTANCE, () -> senderContext, this.observationRegistry);
|
||||
}
|
||||
|
||||
private Producer<T> prepareProducerForSend(@Nullable String topic, T message,
|
||||
private Producer<T> prepareProducerForSend(@Nullable String topic, T message, @Nullable Schema<T> schema,
|
||||
@Nullable Collection<String> encryptionKeys, @Nullable ProducerBuilderCustomizer<T> producerCustomizer)
|
||||
throws PulsarClientException {
|
||||
Schema<T> schema = this.schema != null ? this.schema : this.schemaResolver.getSchema(message);
|
||||
if (schema == null) {
|
||||
schema = Objects.requireNonNull(this.schemaResolver.getSchema(message),
|
||||
"Schema must not be null - expecting at least a default schema");
|
||||
}
|
||||
List<ProducerBuilderCustomizer<T>> customizers = new ArrayList<>();
|
||||
if (!CollectionUtils.isEmpty(this.interceptors)) {
|
||||
customizers.add(builder -> this.interceptors.forEach(builder::intercept));
|
||||
@@ -230,6 +245,9 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
@Nullable
|
||||
private String topic;
|
||||
|
||||
@Nullable
|
||||
private Schema<T> schema;
|
||||
|
||||
@Nullable
|
||||
private Collection<String> encryptionKeys;
|
||||
|
||||
@@ -250,6 +268,12 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendMessageBuilder<T> withSchema(Schema<T> schema) {
|
||||
this.schema = schema;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendMessageBuilder<T> withEncryptionKeys(Collection<String> encryptionKeys) {
|
||||
this.encryptionKeys = encryptionKeys;
|
||||
@@ -270,14 +294,14 @@ public class PulsarTemplate<T> implements PulsarOperations<T>, BeanNameAware {
|
||||
|
||||
@Override
|
||||
public MessageId send() throws PulsarClientException {
|
||||
return this.template.doSend(this.topic, this.encryptionKeys, this.message, this.messageCustomizer,
|
||||
this.producerCustomizer);
|
||||
return this.template.doSend(this.topic, this.message, this.schema, this.encryptionKeys,
|
||||
this.messageCustomizer, this.producerCustomizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageId> sendAsync() throws PulsarClientException {
|
||||
return this.template.doSendAsync(this.topic, this.encryptionKeys, this.message, this.messageCustomizer,
|
||||
this.producerCustomizer);
|
||||
return this.template.doSendAsync(this.topic, this.message, this.schema, this.encryptionKeys,
|
||||
this.messageCustomizer, this.producerCustomizer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,6 +61,117 @@ import org.springframework.pulsar.test.support.PulsarTestContainerSupport;
|
||||
*/
|
||||
class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("sendMessageTestProvider")
|
||||
void sendMessageTest(String testName, SendTestArgs testArgs) throws Exception {
|
||||
// Use the test args to construct the params to pass to send handler
|
||||
String topic = testName;
|
||||
String subscription = topic + "-sub";
|
||||
String msgPayload = topic + "-msg";
|
||||
TypedMessageBuilderCustomizer<String> messageCustomizer = null;
|
||||
if (testArgs.messageCustomizer) {
|
||||
messageCustomizer = (mb) -> mb.key("foo-key");
|
||||
}
|
||||
ProducerBuilderCustomizer<String> producerCustomizer = null;
|
||||
if (testArgs.producerCustomizer) {
|
||||
producerCustomizer = (pb) -> pb.producerName("foo-producer");
|
||||
}
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
try (Consumer<String> consumer = client.newConsumer(Schema.STRING).topic(topic)
|
||||
.subscriptionName(subscription).subscribe()) {
|
||||
Map<String, Object> producerConfig = testArgs.explicitTopic ? Collections.emptyMap()
|
||||
: Collections.singletonMap("topicName", topic);
|
||||
PulsarProducerFactory<String> producerFactory = new DefaultPulsarProducerFactory<>(client,
|
||||
producerConfig);
|
||||
PulsarTemplate<String> pulsarTemplate = new PulsarTemplate<>(producerFactory);
|
||||
|
||||
Object sendResponse;
|
||||
if (testArgs.simpleApi) {
|
||||
if (testArgs.explicitSchema && testArgs.explicitTopic) {
|
||||
sendResponse = testArgs.async ? pulsarTemplate.sendAsync(topic, msgPayload, Schema.STRING)
|
||||
: pulsarTemplate.send(topic, msgPayload, Schema.STRING);
|
||||
}
|
||||
else if (testArgs.explicitSchema) {
|
||||
sendResponse = testArgs.async ? pulsarTemplate.sendAsync(msgPayload, Schema.STRING)
|
||||
: pulsarTemplate.send(msgPayload, Schema.STRING);
|
||||
}
|
||||
else if (testArgs.explicitTopic) {
|
||||
sendResponse = testArgs.async ? pulsarTemplate.sendAsync(topic, msgPayload)
|
||||
: pulsarTemplate.send(topic, msgPayload);
|
||||
}
|
||||
else {
|
||||
sendResponse = testArgs.async ? pulsarTemplate.sendAsync(msgPayload)
|
||||
: pulsarTemplate.send(msgPayload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SendMessageBuilder<String> messageBuilder = pulsarTemplate.newMessage(msgPayload);
|
||||
if (testArgs.explicitTopic) {
|
||||
messageBuilder = messageBuilder.withTopic(topic);
|
||||
}
|
||||
if (testArgs.explicitSchema) {
|
||||
messageBuilder = messageBuilder.withSchema(Schema.STRING);
|
||||
}
|
||||
if (messageCustomizer != null) {
|
||||
messageBuilder = messageBuilder.withMessageCustomizer(messageCustomizer);
|
||||
}
|
||||
if (producerCustomizer != null) {
|
||||
messageBuilder = messageBuilder.withProducerCustomizer(producerCustomizer);
|
||||
}
|
||||
sendResponse = testArgs.async ? messageBuilder.sendAsync() : messageBuilder.send();
|
||||
}
|
||||
|
||||
if (sendResponse instanceof CompletableFuture) {
|
||||
sendResponse = ((CompletableFuture<?>) sendResponse).get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
assertThat(sendResponse).isNotNull();
|
||||
|
||||
CompletableFuture<Message<String>> receiveMsgFuture = consumer.receiveAsync();
|
||||
Message<String> msg = receiveMsgFuture.get(3, TimeUnit.SECONDS);
|
||||
|
||||
assertThat(msg.getData()).asString().isEqualTo(msgPayload);
|
||||
if (messageCustomizer != null) {
|
||||
assertThat(msg.getKey()).isEqualTo("foo-key");
|
||||
}
|
||||
if (producerCustomizer != null) {
|
||||
assertThat(msg.getProducerName()).isEqualTo("foo-producer");
|
||||
}
|
||||
// Make sure the producer was closed by the template (albeit indirectly as
|
||||
// client removes closed producers)
|
||||
await().atMost(Duration.ofSeconds(3)).untilAsserted(() -> assertThat(client).extracting("producers")
|
||||
.asInstanceOf(InstanceOfAssertFactories.COLLECTION).isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Arguments> sendMessageTestProvider() {
|
||||
return Stream.of(arguments("simpleSend", SendTestArgs.simple().sync()),
|
||||
arguments("simpleSendWithTopic", SendTestArgs.simple().sync().topic()),
|
||||
arguments("simpleSendWithSchema", SendTestArgs.simple().sync().schema()),
|
||||
arguments("simpleSendWithTopicAndSchema", SendTestArgs.simple().sync().topic().schema()),
|
||||
arguments("simpleAsyncSend", SendTestArgs.simple().async()),
|
||||
arguments("simpleAsyncSendWithTopic", SendTestArgs.simple().async().topic()),
|
||||
arguments("simpleAsyncSendWithSchema", SendTestArgs.simple().async().schema()),
|
||||
arguments("simpleAsyncSendWithTopicAndSchema", SendTestArgs.simple().async().topic().schema()),
|
||||
arguments("fluentSend", SendTestArgs.fluent().sync()),
|
||||
arguments("fluentSendWithSchema", SendTestArgs.fluent().sync().schema()),
|
||||
arguments("fluentSendWithTopic", SendTestArgs.fluent().sync().topic()),
|
||||
arguments("fluentSendWithMessageCustomizer", SendTestArgs.fluent().sync().messageCustomizer()),
|
||||
arguments("fluentSendWithProducerCustomizer", SendTestArgs.fluent().sync().producerCustomizer()),
|
||||
arguments("fluentSendWithTopicAndSchema", SendTestArgs.fluent().sync().topic().schema()),
|
||||
arguments("fluentSendWithTopicAndSchemaAndCustomizers",
|
||||
SendTestArgs.fluent().sync().topic().schema().messageCustomizer().producerCustomizer()),
|
||||
arguments("fluentAsyncSend", SendTestArgs.fluent().async()),
|
||||
arguments("fluentAsyncSendWithSchema", SendTestArgs.fluent().async().schema()),
|
||||
arguments("fluentAsyncSendWithTopic", SendTestArgs.fluent().async().topic()),
|
||||
arguments("fluentAsyncSendWithMessageCustomizer", SendTestArgs.fluent().async().messageCustomizer()),
|
||||
arguments("fluentAsyncSendWithProducerCustomizer", SendTestArgs.fluent().async().producerCustomizer()),
|
||||
arguments("fluentAsyncSendWithTopicAndSchema", SendTestArgs.fluent().async().topic().schema()),
|
||||
arguments("fluentAsyncSendWithTopicAndSchemaAndCustomizers",
|
||||
SendTestArgs.fluent().async().topic().schema().messageCustomizer().producerCustomizer()));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("interceptorInvocationTestProvider")
|
||||
void interceptorInvocationTest(String topic, List<ProducerInterceptor> interceptors) throws Exception {
|
||||
@@ -78,9 +189,9 @@ class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
|
||||
private static Stream<Arguments> interceptorInvocationTestProvider() {
|
||||
return Stream.of(
|
||||
arguments(Named.of("testSingleInterceptor", "iit-topic-1"),
|
||||
arguments(Named.of("singleInterceptor", "iit-topic-1"),
|
||||
Collections.singletonList(mock(ProducerInterceptor.class))),
|
||||
arguments(Named.of("testMultipleInterceptors", "iit-topic-2"),
|
||||
arguments(Named.of("multipleInterceptors", "iit-topic-2"),
|
||||
List.of(mock(ProducerInterceptor.class), mock(ProducerInterceptor.class))));
|
||||
}
|
||||
|
||||
@@ -94,9 +205,8 @@ class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<Foo> producerFactory = new DefaultPulsarProducerFactory<>(client,
|
||||
Collections.singletonMap("topicName", topic));
|
||||
PulsarTemplate<Foo> pulsarTemplate = new PulsarTemplate<>(producerFactory);
|
||||
pulsarTemplate.setSchema(Schema.JSON(Foo.class));
|
||||
Foo foo = new Foo("Foo-" + UUID.randomUUID(), "Bar-" + UUID.randomUUID());
|
||||
pulsarTemplate.send(foo);
|
||||
pulsarTemplate.send(foo, Schema.JSON(Foo.class));
|
||||
assertThat(consumer.receiveAsync()).succeedsWithin(Duration.ofSeconds(3)).extracting(Message::getValue)
|
||||
.isEqualTo(foo);
|
||||
}
|
||||
@@ -104,7 +214,7 @@ class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendMessageWithSpecificSchemaAndCustomTypeMappings() throws Exception {
|
||||
void sendMessageWithSpecificSchemaInferredByCustomTypeMappings() throws Exception {
|
||||
String topic = "smt-specific-schema-custom-topic";
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
@@ -112,7 +222,7 @@ class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
.subscriptionName("smt-specific-schema-custom-subscription").subscribe()) {
|
||||
PulsarProducerFactory<Foo> producerFactory = new DefaultPulsarProducerFactory<>(client,
|
||||
Collections.singletonMap("topicName", topic));
|
||||
// Custom schema resolver allows not calling setSchema on template
|
||||
// Custom schema resolver allows not specifying the schema when sending
|
||||
DefaultSchemaResolver schemaResolver = new DefaultSchemaResolver();
|
||||
schemaResolver.addCustomSchemaMapping(Foo.class, Schema.JSON(Foo.class));
|
||||
PulsarTemplate<Foo> pulsarTemplate = new PulsarTemplate<>(producerFactory, Collections.emptyList(),
|
||||
@@ -141,156 +251,59 @@ class PulsarTemplateTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("sendMessageTestProvider")
|
||||
void sendMessageTest(String testName, SendTestArgs testArgs) throws Exception {
|
||||
// Use the test args to construct the params to pass to send handler
|
||||
String topic = testName;
|
||||
String subscription = topic + "-sub";
|
||||
String msgPayload = topic + "-msg";
|
||||
TypedMessageBuilderCustomizer<String> messageCustomizer = null;
|
||||
if (testArgs.useMessageCustomizer) {
|
||||
messageCustomizer = (mb) -> mb.key("foo-key");
|
||||
}
|
||||
ProducerBuilderCustomizer<String> producerCustomizer = null;
|
||||
if (testArgs.useProducerCustomizer) {
|
||||
producerCustomizer = (pb) -> pb.producerName("foo-producer");
|
||||
}
|
||||
|
||||
try (PulsarClient client = PulsarClient.builder().serviceUrl(PulsarTestContainerSupport.getPulsarBrokerUrl())
|
||||
.build()) {
|
||||
try (Consumer<String> consumer = client.newConsumer(Schema.STRING).topic(topic)
|
||||
.subscriptionName(subscription).subscribe()) {
|
||||
Map<String, Object> producerConfig = testArgs.useSpecificTopic ? Collections.emptyMap()
|
||||
: Collections.singletonMap("topicName", topic);
|
||||
PulsarProducerFactory<String> producerFactory = new DefaultPulsarProducerFactory<>(client,
|
||||
producerConfig);
|
||||
PulsarTemplate<String> pulsarTemplate = new PulsarTemplate<>(producerFactory);
|
||||
Object sendResponse;
|
||||
if (testArgs.useSimpleApi) {
|
||||
if (testArgs.useAsyncSend) {
|
||||
sendResponse = testArgs.useSpecificTopic ? pulsarTemplate.sendAsync(topic, msgPayload)
|
||||
: pulsarTemplate.sendAsync(msgPayload);
|
||||
}
|
||||
else {
|
||||
sendResponse = testArgs.useSpecificTopic ? pulsarTemplate.send(topic, msgPayload)
|
||||
: pulsarTemplate.send(msgPayload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SendMessageBuilder<String> messageBuilder = pulsarTemplate.newMessage(msgPayload);
|
||||
if (testArgs.useSpecificTopic) {
|
||||
messageBuilder = messageBuilder.withTopic(topic);
|
||||
}
|
||||
if (messageCustomizer != null) {
|
||||
messageBuilder = messageBuilder.withMessageCustomizer(messageCustomizer);
|
||||
}
|
||||
if (producerCustomizer != null) {
|
||||
messageBuilder = messageBuilder.withProducerCustomizer(producerCustomizer);
|
||||
}
|
||||
sendResponse = testArgs.useAsyncSend ? messageBuilder.sendAsync() : messageBuilder.send();
|
||||
}
|
||||
|
||||
if (sendResponse instanceof CompletableFuture) {
|
||||
sendResponse = ((CompletableFuture<?>) sendResponse).get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
assertThat(sendResponse).isNotNull();
|
||||
|
||||
CompletableFuture<Message<String>> receiveMsgFuture = consumer.receiveAsync();
|
||||
Message<String> msg = receiveMsgFuture.get(3, TimeUnit.SECONDS);
|
||||
|
||||
assertThat(msg.getData()).asString().isEqualTo(msgPayload);
|
||||
if (messageCustomizer != null) {
|
||||
assertThat(msg.getKey()).isEqualTo("foo-key");
|
||||
}
|
||||
if (producerCustomizer != null) {
|
||||
assertThat(msg.getProducerName()).isEqualTo("foo-producer");
|
||||
}
|
||||
// Make sure the producer was closed by the template (albeit indirectly as
|
||||
// client removes closed producers)
|
||||
await().atMost(Duration.ofSeconds(3)).untilAsserted(() -> assertThat(client).extracting("producers")
|
||||
.asInstanceOf(InstanceOfAssertFactories.COLLECTION).isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Arguments> sendMessageTestProvider() {
|
||||
return Stream.of(arguments("sendMessageToDefaultTopic", SendTestArgs.useSpecificTopic(false)),
|
||||
arguments("sendMessageToDefaultTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(false).useSimpleApi()),
|
||||
arguments("sendMessageToDefaultTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer()),
|
||||
arguments("sendMessageToDefaultTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useProducerCustomizer()),
|
||||
arguments("sendMessageToDefaultTopicWithAllOptions",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer().useProducerCustomizer()),
|
||||
arguments("sendMessageToSpecificTopic", SendTestArgs.useSpecificTopic(true)),
|
||||
arguments("sendMessageToSpecificTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(true).useSimpleApi()),
|
||||
arguments("sendMessageToSpecificTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useMessageCustomizer()),
|
||||
arguments("sendMessageToSpecificTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useProducerCustomizer()),
|
||||
arguments("sendMessageToSpecificTopicWithAllOptions",
|
||||
SendTestArgs.useSpecificTopic(true).useMessageCustomizer().useProducerCustomizer()),
|
||||
arguments("sendAsyncMessageToDefaultTopic", SendTestArgs.useSpecificTopic(false).useAsyncSend()),
|
||||
arguments("sendAsyncMessageToDefaultTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(false).useAsyncSend().useSimpleApi()),
|
||||
arguments("sendAsyncMessageToDefaultTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer().useAsyncSend()),
|
||||
arguments("sendAsyncMessageToDefaultTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(false).useProducerCustomizer().useAsyncSend()),
|
||||
arguments("sendAsyncMessageToDefaultTopicWithAllOptions",
|
||||
SendTestArgs.useSpecificTopic(false).useMessageCustomizer().useProducerCustomizer()
|
||||
.useAsyncSend()),
|
||||
arguments("sendAsyncMessageToSpecificTopic", SendTestArgs.useSpecificTopic(true).useAsyncSend()),
|
||||
arguments("sendAsyncMessageToSpecificTopicWithSimpleApi",
|
||||
SendTestArgs.useSpecificTopic(true).useAsyncSend().useSimpleApi()),
|
||||
arguments("sendAsyncMessageToSpecificTopicWithMessageCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useMessageCustomizer().useAsyncSend()),
|
||||
arguments("sendAsyncMessageToSpecificTopicWithProducerCustomizer",
|
||||
SendTestArgs.useSpecificTopic(true).useProducerCustomizer().useAsyncSend()),
|
||||
arguments("sendAsyncMessageToSpecificTopicWithAllOptions", SendTestArgs.useSpecificTopic(true)
|
||||
.useMessageCustomizer().useProducerCustomizer().useAsyncSend()));
|
||||
}
|
||||
|
||||
static final class SendTestArgs {
|
||||
|
||||
private final boolean useSpecificTopic;
|
||||
private final boolean simpleApi;
|
||||
|
||||
private boolean useMessageCustomizer;
|
||||
private boolean async;
|
||||
|
||||
private boolean useProducerCustomizer;
|
||||
private boolean explicitTopic;
|
||||
|
||||
private boolean useAsyncSend;
|
||||
private boolean explicitSchema;
|
||||
|
||||
private boolean useSimpleApi;
|
||||
private boolean messageCustomizer;
|
||||
|
||||
private SendTestArgs(boolean useSpecificTopic) {
|
||||
this.useSpecificTopic = useSpecificTopic;
|
||||
private boolean producerCustomizer;
|
||||
|
||||
private SendTestArgs(boolean simpleApi) {
|
||||
this.simpleApi = simpleApi;
|
||||
}
|
||||
|
||||
static SendTestArgs useSpecificTopic(boolean useSpecificTopic) {
|
||||
return new SendTestArgs(useSpecificTopic);
|
||||
static SendTestArgs simple() {
|
||||
return new SendTestArgs(true);
|
||||
}
|
||||
|
||||
SendTestArgs useMessageCustomizer() {
|
||||
this.useMessageCustomizer = true;
|
||||
static SendTestArgs fluent() {
|
||||
return new SendTestArgs(false);
|
||||
}
|
||||
|
||||
SendTestArgs async() {
|
||||
this.async = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useProducerCustomizer() {
|
||||
this.useProducerCustomizer = true;
|
||||
SendTestArgs sync() {
|
||||
this.async = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useAsyncSend() {
|
||||
this.useAsyncSend = true;
|
||||
SendTestArgs topic() {
|
||||
this.explicitTopic = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs useSimpleApi() {
|
||||
this.useSimpleApi = true;
|
||||
SendTestArgs schema() {
|
||||
this.explicitSchema = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs messageCustomizer() {
|
||||
this.messageCustomizer = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
SendTestArgs producerCustomizer() {
|
||||
this.producerCustomizer = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ import org.springframework.util.backoff.FixedBackOff;
|
||||
/**
|
||||
* @author Soby Chacko
|
||||
* @author Alexander Preuß
|
||||
* @author Chris Bono
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
@@ -230,26 +231,26 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
static class TestPulsarListenersForBasicScenario {
|
||||
|
||||
@PulsarListener(id = "foo", properties = { "subscriptionName=subscription-1", "topicNames=foo-1" })
|
||||
void listen1(String message) {
|
||||
void listen1(String ignored) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "bar", topics = "concurrency-on-pl", subscriptionName = "subscription-2",
|
||||
subscriptionType = SubscriptionType.Failover, concurrency = "3")
|
||||
void listen2(String message) {
|
||||
void listen2(String ignored) {
|
||||
latch1.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "baz", topicPattern = "persistent://public/default/pattern.*",
|
||||
subscriptionName = "subscription-3",
|
||||
properties = { "patternAutoDiscoveryPeriod=5", "subscriptionInitialPosition=Earliest" })
|
||||
void listen3(String message) {
|
||||
void listen3(String ignored) {
|
||||
latch2.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "ackMode-test-id", subscriptionName = "ackModeTest-sub", topics = "ackModeTest-topic",
|
||||
ackMode = AckMode.RECORD)
|
||||
void ackModeTestListener(String message) {
|
||||
void ackModeTestListener(String ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -263,8 +264,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
static CountDownLatch nackRedeliveryBackoffLatch = new CountDownLatch(5);
|
||||
|
||||
@Test
|
||||
void pulsarListenerWithNackRedeliveryBackoff(@Autowired PulsarListenerEndpointRegistry registry)
|
||||
throws Exception {
|
||||
void pulsarListenerWithNackRedeliveryBackoff() throws Exception {
|
||||
pulsarTemplate.send("withNegRedeliveryBackoff-test-topic", "hello john doe");
|
||||
assertThat(nackRedeliveryBackoffLatch.await(15, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -298,8 +298,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
static CountDownLatch ackTimeoutRedeliveryBackoffLatch = new CountDownLatch(5);
|
||||
|
||||
@Test
|
||||
void pulsarListenerWithAckTimeoutRedeliveryBackoff(@Autowired PulsarListenerEndpointRegistry registry)
|
||||
throws Exception {
|
||||
void pulsarListenerWithAckTimeoutRedeliveryBackoff() throws Exception {
|
||||
pulsarTemplate.send("withAckTimeoutRedeliveryBackoff-test-topic", "hello john doe");
|
||||
assertThat(ackTimeoutRedeliveryBackoffLatch.await(60, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -313,7 +312,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
topics = "withAckTimeoutRedeliveryBackoff-test-topic",
|
||||
ackTimeoutRedeliveryBackoff = "ackTimeoutRedeliveryBackoff",
|
||||
subscriptionType = SubscriptionType.Shared, properties = { "ackTimeoutMillis=1000" })
|
||||
void listen(String msg) {
|
||||
void listen(String ignored) {
|
||||
ackTimeoutRedeliveryBackoffLatch.countDown();
|
||||
throw new RuntimeException();
|
||||
}
|
||||
@@ -399,7 +398,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
}
|
||||
|
||||
@PulsarListener(id = "dlqListener", topics = "dlpt-dlq-topic")
|
||||
void listenDlq(String msg) {
|
||||
void listenDlq(String ignored) {
|
||||
dlqLatch.countDown();
|
||||
}
|
||||
|
||||
@@ -428,7 +427,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "foobar", topics = "concurrency-on-pl", subscriptionName = "subscription-3",
|
||||
concurrency = "3")
|
||||
void listen3(String message) {
|
||||
void listen3(String ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -453,10 +452,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(JSONSchema.of(User.class));
|
||||
|
||||
Schema<User> schema = JSONSchema.of(User.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("json-topic", new User("Jason", i));
|
||||
template.send("json-topic", new User("Jason", i), schema);
|
||||
}
|
||||
assertThat(jsonLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(jsonBatchLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
@@ -467,10 +465,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(AvroSchema.of(User.class));
|
||||
|
||||
Schema<User> schema = AvroSchema.of(User.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("avro-topic", new User("Avi", i));
|
||||
template.send("avro-topic", new User("Avi", i), schema);
|
||||
}
|
||||
assertThat(avroLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(avroBatchLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
@@ -483,10 +480,8 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarTemplate<KeyValue<String, Integer>> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
Schema<KeyValue<String, Integer>> kvSchema = Schema.KeyValue(Schema.STRING, Schema.INT32,
|
||||
KeyValueEncodingType.INLINE);
|
||||
template.setSchema(kvSchema);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("keyvalue-topic", new KeyValue<>("Kevin", i));
|
||||
template.send("keyvalue-topic", new KeyValue<>("Kevin", i), kvSchema);
|
||||
}
|
||||
assertThat(keyvalueLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(keyvalueBatchLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
@@ -497,10 +492,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<Proto.Person> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<Proto.Person> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(ProtobufSchema.of(Proto.Person.class));
|
||||
|
||||
Schema<Proto.Person> schema = ProtobufSchema.of(Proto.Person.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("protobuf-topic", Proto.Person.newBuilder().setId(i).setName("Paul").build());
|
||||
template.send("protobuf-topic", Proto.Person.newBuilder().setId(i).setName("Paul").build(), schema);
|
||||
}
|
||||
assertThat(protobufLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(protobufBatchLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
@@ -512,7 +506,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "jsonListener", topics = "json-topic", subscriptionName = "subscription-4",
|
||||
schemaType = SchemaType.JSON, properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenJson(User message) {
|
||||
void listenJson(User ignored) {
|
||||
jsonLatch.countDown();
|
||||
}
|
||||
|
||||
@@ -524,7 +518,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "avroListener", topics = "avro-topic", subscriptionName = "subscription-6",
|
||||
schemaType = SchemaType.AVRO, properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenAvro(User message) {
|
||||
void listenAvro(User ignored) {
|
||||
avroLatch.countDown();
|
||||
}
|
||||
|
||||
@@ -536,7 +530,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "keyvalueListener", topics = "keyvalue-topic", subscriptionName = "subscription-8",
|
||||
schemaType = SchemaType.KEY_VALUE, properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenKeyvalue(KeyValue<String, Integer> message) {
|
||||
void listenKeyvalue(KeyValue<String, Integer> ignored) {
|
||||
keyvalueLatch.countDown();
|
||||
}
|
||||
|
||||
@@ -549,7 +543,7 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "protobufListener", topics = "protobuf-topic", subscriptionName = "subscription-10",
|
||||
schemaType = SchemaType.PROTOBUF, properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenProtobuf(Proto.Person message) {
|
||||
void listenProtobuf(Proto.Person ignored) {
|
||||
protobufLatch.countDown();
|
||||
}
|
||||
|
||||
@@ -635,10 +629,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User2> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User2> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(Schema.JSON(User2.class));
|
||||
|
||||
Schema<User2> schema = Schema.JSON(User2.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("json-custom-mappings-topic", new User2("Jason", i));
|
||||
template.send("json-custom-mappings-topic", new User2("Jason", i), schema);
|
||||
}
|
||||
assertThat(jsonLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -648,10 +641,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<User> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<User> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(AvroSchema.of(User.class));
|
||||
|
||||
Schema<User> schema = AvroSchema.of(User.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("avro-custom-mappings-topic", new User("Avi", i));
|
||||
template.send("avro-custom-mappings-topic", new User("Avi", i), schema);
|
||||
}
|
||||
assertThat(avroLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -663,10 +655,9 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarTemplate<KeyValue<String, User2>> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
Schema<KeyValue<String, User2>> kvSchema = Schema.KeyValue(Schema.STRING, Schema.JSON(User2.class),
|
||||
KeyValueEncodingType.INLINE);
|
||||
template.setSchema(kvSchema);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("keyvalue-custom-mappings-topic", new KeyValue<>("Kevin", new User2("Kevin", 5150)));
|
||||
template.send("keyvalue-custom-mappings-topic", new KeyValue<>("Kevin", new User2("Kevin", 5150)),
|
||||
kvSchema);
|
||||
}
|
||||
assertThat(keyvalueLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -676,11 +667,10 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
PulsarProducerFactory<Proto.Person> pulsarProducerFactory = new DefaultPulsarProducerFactory<>(pulsarClient,
|
||||
Collections.emptyMap());
|
||||
PulsarTemplate<Proto.Person> template = new PulsarTemplate<>(pulsarProducerFactory);
|
||||
template.setSchema(ProtobufSchema.of(Proto.Person.class));
|
||||
|
||||
Schema<Proto.Person> schema = ProtobufSchema.of(Proto.Person.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
template.send("protobuf-custom-mappings-topic",
|
||||
Proto.Person.newBuilder().setId(i).setName("Paul").build());
|
||||
Proto.Person.newBuilder().setId(i).setName("Paul").build(), schema);
|
||||
}
|
||||
assertThat(protobufLatch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
}
|
||||
@@ -710,25 +700,25 @@ public class PulsarListenerTests implements PulsarTestContainerSupport {
|
||||
|
||||
@PulsarListener(id = "jsonListener", topics = "json-custom-mappings-topic",
|
||||
subscriptionName = "subscription-4", properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenJson(User2 message) {
|
||||
void listenJson(User2 ignored) {
|
||||
jsonLatch.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "avroListener", topics = "avro-custom-mappings-topic",
|
||||
subscriptionName = "subscription-6", properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenAvro(User message) {
|
||||
void listenAvro(User ignored) {
|
||||
avroLatch.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "keyvalueListener", topics = "keyvalue-custom-mappings-topic",
|
||||
subscriptionName = "subscription-8", properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenKeyvalue(KeyValue<String, User2> message) {
|
||||
void listenKeyvalue(KeyValue<String, User2> ignored) {
|
||||
keyvalueLatch.countDown();
|
||||
}
|
||||
|
||||
@PulsarListener(id = "protobufListener", topics = "protobuf-custom-mappings-topic",
|
||||
subscriptionName = "subscription-10", properties = { "subscriptionInitialPosition=Earliest" })
|
||||
void listenProtobuf(Proto.Person message) {
|
||||
void listenProtobuf(Proto.Person ignored) {
|
||||
protobufLatch.countDown();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user