Merge pull request #758 from sobychacko/gh-757
Function level binding properties
This commit is contained in:
@@ -157,6 +157,25 @@ public abstract class AbstractKafkaStreamsBinderProcessor implements Application
|
||||
Map<String, Object> streamConfigGlobalProperties = applicationContext
|
||||
.getBean("streamConfigGlobalProperties", Map.class);
|
||||
|
||||
if (kafkaStreamsBinderConfigurationProperties != null) {
|
||||
final Map<String, KafkaStreamsBinderConfigurationProperties.Functions> functionConfigMap = kafkaStreamsBinderConfigurationProperties.getFunctions();
|
||||
if (!CollectionUtils.isEmpty(functionConfigMap)) {
|
||||
final KafkaStreamsBinderConfigurationProperties.Functions functionConfig = functionConfigMap.get(beanNamePostPrefix);
|
||||
final Map<String, String> functionSpecificConfig = functionConfig.getConfiguration();
|
||||
if (!CollectionUtils.isEmpty(functionSpecificConfig)) {
|
||||
streamConfigGlobalProperties.putAll(functionSpecificConfig);
|
||||
}
|
||||
|
||||
String applicationId = functionConfig.getApplicationId();
|
||||
if (!StringUtils.isEmpty(applicationId)) {
|
||||
streamConfigGlobalProperties.put(StreamsConfig.APPLICATION_ID_CONFIG, applicationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//this is only used primarily for StreamListener based processors. Although in theory, functions can use it,
|
||||
//it is ideal for functions to use the approach used in the above if statement by using a property like
|
||||
//spring.cloud.stream.kafka.streams.binder.functions.process.configuration.num.threads (assuming that process is the function name).
|
||||
KafkaStreamsConsumerProperties extendedConsumerProperties = this.kafkaStreamsExtendedBindingProperties
|
||||
.getExtendedConsumerProperties(inboundName);
|
||||
streamConfigGlobalProperties
|
||||
@@ -165,17 +184,12 @@ public abstract class AbstractKafkaStreamsBinderProcessor implements Application
|
||||
String bindingLevelApplicationId = extendedConsumerProperties.getApplicationId();
|
||||
// override application.id if set at the individual binding level.
|
||||
// We provide this for backward compatibility with StreamListener based processors.
|
||||
// For function based processors see the next else if conditional block
|
||||
// For function based processors see the approach used above
|
||||
// (i.e. use a property like spring.cloud.stream.kafka.streams.binder.functions.process.applicationId).
|
||||
if (StringUtils.hasText(bindingLevelApplicationId)) {
|
||||
streamConfigGlobalProperties.put(StreamsConfig.APPLICATION_ID_CONFIG,
|
||||
bindingLevelApplicationId);
|
||||
}
|
||||
else if (kafkaStreamsBinderConfigurationProperties != null && !CollectionUtils.isEmpty(kafkaStreamsBinderConfigurationProperties.getFunctions())) {
|
||||
String applicationId = kafkaStreamsBinderConfigurationProperties.getFunctions().get(beanNamePostPrefix + ".applicationId");
|
||||
if (!StringUtils.isEmpty(applicationId)) {
|
||||
streamConfigGlobalProperties.put(StreamsConfig.APPLICATION_ID_CONFIG, applicationId);
|
||||
}
|
||||
}
|
||||
|
||||
//If the application id is not set by any mechanism, then generate it.
|
||||
streamConfigGlobalProperties.computeIfAbsent(StreamsConfig.APPLICATION_ID_CONFIG,
|
||||
|
||||
@@ -57,10 +57,18 @@ public class KafkaStreamsBinderConfigurationProperties
|
||||
|
||||
private String applicationId;
|
||||
|
||||
private Map<String, String> functions = new HashMap<>();
|
||||
|
||||
private StateStoreRetry stateStoreRetry = new StateStoreRetry();
|
||||
|
||||
private Map<String, Functions> functions = new HashMap<>();
|
||||
|
||||
public Map<String, Functions> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public void setFunctions(Map<String, Functions> functions) {
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
public StateStoreRetry getStateStoreRetry() {
|
||||
return stateStoreRetry;
|
||||
}
|
||||
@@ -69,14 +77,6 @@ public class KafkaStreamsBinderConfigurationProperties
|
||||
this.stateStoreRetry = stateStoreRetry;
|
||||
}
|
||||
|
||||
public Map<String, String> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public void setFunctions(Map<String, String> functions) {
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
public String getApplicationId() {
|
||||
return this.applicationId;
|
||||
}
|
||||
@@ -125,4 +125,33 @@ public class KafkaStreamsBinderConfigurationProperties
|
||||
}
|
||||
}
|
||||
|
||||
public static class Functions {
|
||||
|
||||
/**
|
||||
* Function specific application id.
|
||||
*/
|
||||
private String applicationId;
|
||||
|
||||
/**
|
||||
* Funcion specific configuraiton to use.
|
||||
*/
|
||||
private Map<String, String> configuration;
|
||||
|
||||
public String getApplicationId() {
|
||||
return applicationId;
|
||||
}
|
||||
|
||||
public void setApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public void setConfiguration(Map<String, String> configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,9 +91,7 @@ public class KafkaStreamsBinderWordCountBranchesFunctionTests {
|
||||
"=org.apache.kafka.common.serialization.Serdes$StringSerde",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde" +
|
||||
"=org.apache.kafka.common.serialization.Serdes$StringSerde",
|
||||
"--spring.cloud.stream.kafka.streams.timeWindow.length=5000",
|
||||
"--spring.cloud.stream.kafka.streams.timeWindow.advanceBy=0",
|
||||
"--spring.cloud.stream.kafka.streams.bindings.input.consumer.applicationId" +
|
||||
"--spring.cloud.stream.kafka.streams.binder.applicationId" +
|
||||
"=KafkaStreamsBinderWordCountBranchesFunctionTests-abc",
|
||||
"--spring.cloud.stream.kafka.streams.binder.brokers=" + embeddedKafka.getBrokersAsString());
|
||||
try {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class KafkaStreamsFunctionStateStoreTests {
|
||||
try (ConfigurableApplicationContext context = app.run("--server.port=0",
|
||||
"--spring.jmx.enabled=false",
|
||||
"--spring.cloud.stream.bindings.input.destination=words",
|
||||
"--spring.cloud.stream.kafka.streams.default.consumer.application-id=testKafkaStreamsFuncionWithMultipleStateStores",
|
||||
"--spring.cloud.stream.kafka.streams.binder.application-id=testKafkaStreamsFuncionWithMultipleStateStores",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=1000",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde" +
|
||||
"=org.apache.kafka.common.serialization.Serdes$StringSerde",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.stream.binder.kafka.streams.function;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.kafka.config.StreamsBuilderFactoryBean;
|
||||
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
|
||||
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
@@ -78,7 +80,7 @@ public class MultipleFunctionsInSameAppTests {
|
||||
SpringApplication app = new SpringApplication(MultipleFunctionsInSameApp.class);
|
||||
app.setWebApplicationType(WebApplicationType.NONE);
|
||||
|
||||
try (ConfigurableApplicationContext ignored = app.run(
|
||||
try (ConfigurableApplicationContext context = app.run(
|
||||
"--server.port=0",
|
||||
"--spring.jmx.enabled=false",
|
||||
"--spring.cloud.stream.bindings.process-in-0.destination=purchases",
|
||||
@@ -89,12 +91,22 @@ public class MultipleFunctionsInSameAppTests {
|
||||
"--spring.cloud.stream.kafka.streams.binder.functions.analyze.applicationId=analyze-id-0",
|
||||
"--spring.cloud.stream.kafka.streams.binder.functions.process.applicationId=process-id-0",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=1000",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde" +
|
||||
"=org.apache.kafka.common.serialization.Serdes$StringSerde",
|
||||
"--spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde" +
|
||||
"=org.apache.kafka.common.serialization.Serdes$StringSerde",
|
||||
"--spring.cloud.stream.kafka.streams.binder.functions.process.configuration.client.id=process-client",
|
||||
"--spring.cloud.stream.kafka.streams.binder.functions.analyze.configuration.client.id=analyze-client",
|
||||
"--spring.cloud.stream.kafka.streams.binder.brokers=" + embeddedKafka.getBrokersAsString())) {
|
||||
receiveAndValidate("purchases", "coffee", "electronics");
|
||||
|
||||
StreamsBuilderFactoryBean processStreamsBuilderFactoryBean = context
|
||||
.getBean("&stream-builder-process", StreamsBuilderFactoryBean.class);
|
||||
|
||||
StreamsBuilderFactoryBean analyzeStreamsBuilderFactoryBean = context
|
||||
.getBean("&stream-builder-analyze", StreamsBuilderFactoryBean.class);
|
||||
|
||||
final Properties processStreamsConfiguration = processStreamsBuilderFactoryBean.getStreamsConfiguration();
|
||||
final Properties analyzeStreamsConfiguration = analyzeStreamsBuilderFactoryBean.getStreamsConfiguration();
|
||||
|
||||
assertThat(processStreamsConfiguration.getProperty("client.id")).isEqualTo("process-client");
|
||||
assertThat(analyzeStreamsConfiguration.getProperty("client.id")).isEqualTo("analyze-client");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user