diff --git a/pom.xml b/pom.xml
index ae6272583..3d1218dcd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
org.springframework.cloud
spring-cloud-stream-parent
- 1.1.0.M1
+ 1.1.0.BUILD-SNAPSHOT
@@ -19,8 +19,8 @@
spring-cloud-stream-binder-kafka
spring-cloud-starter-stream-kafka
- spring-cloud-stream-binder-kafka-test-support
spring-cloud-stream-binder-kafka-docs
+ spring-cloud-stream-binder-kafka-0.10-test
diff --git a/spring-cloud-starter-stream-kafka/pom.xml b/spring-cloud-starter-stream-kafka/pom.xml
index b1d327de7..ae85746a4 100644
--- a/spring-cloud-starter-stream-kafka/pom.xml
+++ b/spring-cloud-starter-stream-kafka/pom.xml
@@ -15,12 +15,42 @@
${basedir}/../..
+ 0.9.0.1
+ 1.0.3.RELEASE
+ 2.0.1.RELEASE
+
org.springframework.cloud
spring-cloud-stream-binder-kafka
- 1.1.0.BUILD-SNAPSHOT
+ ${project.version}
+
+
+ org.springframework.kafka
+ spring-kafka
+ ${spring-kafka.version}
+
+
+ org.apache.kafka
+ kafka_2.11
+ ${kafka.version}
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+
+
+ org.springframework.integration
+ spring-integration-kafka
+ ${spring-integration-kafka.version}
diff --git a/spring-cloud-starter-stream-kafka/src/main/resources/META-INF/spring.provides b/spring-cloud-starter-stream-kafka/src/main/resources/META-INF/spring.provides
index cc7cb9cc2..d9ca8793e 100644
--- a/spring-cloud-starter-stream-kafka/src/main/resources/META-INF/spring.provides
+++ b/spring-cloud-starter-stream-kafka/src/main/resources/META-INF/spring.provides
@@ -1 +1 @@
-provides: spring-cloud-starter-stream-kafka
\ No newline at end of file
+provides: spring-cloud-starter-stream-kafka-0.10
\ No newline at end of file
diff --git a/spring-cloud-stream-binder-kafka-0.10-test/pom.xml b/spring-cloud-stream-binder-kafka-0.10-test/pom.xml
new file mode 100644
index 000000000..92aa97f58
--- /dev/null
+++ b/spring-cloud-stream-binder-kafka-0.10-test/pom.xml
@@ -0,0 +1,80 @@
+
+
+ 4.0.0
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-kafka-parent
+ 1.1.0.BUILD-SNAPSHOT
+
+ spring-cloud-stream-binder-kafka-0.10-test
+ Spring Cloud Stream Kafka Binder 0.10 Tests
+ http://projects.spring.io/spring-cloud
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+ 0.10.0.0
+ 1.1.0.M1
+ 2.0.1.RELEASE
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-kafka
+ ${project.version}
+ test
+
+
+ org.springframework.kafka
+ spring-kafka
+ ${spring-kafka.version}
+ test
+
+
+ org.apache.kafka
+ kafka_2.11
+ ${kafka.version}
+ test
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+ test
+
+
+ org.springframework.kafka
+ spring-kafka-test
+ test
+ ${spring-kafka.version}
+
+
+ org.springframework.integration
+ spring-integration-kafka
+ ${spring-integration-kafka.version}
+
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-kafka
+ ${project.version}
+ test-jar
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-test
+ test
+
+
+
+
+
diff --git a/spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10BinderTests.java b/spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10BinderTests.java
new file mode 100644
index 000000000..26f3533bc
--- /dev/null
+++ b/spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10BinderTests.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2014-2016 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.kafka;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import kafka.utils.ZKStringSerializer$;
+import kafka.utils.ZkUtils;
+import org.I0Itec.zkclient.ZkClient;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.ByteArrayDeserializer;
+import org.apache.kafka.common.serialization.Deserializer;
+import org.junit.Before;
+import org.junit.ClassRule;
+
+import org.springframework.cloud.stream.binder.Binder;
+import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
+import org.springframework.cloud.stream.binder.ExtendedProducerProperties;
+import org.springframework.cloud.stream.binder.Spy;
+import org.springframework.cloud.stream.binder.kafka.admin.Kafka10AdminUtilsOperation;
+import org.springframework.cloud.stream.binder.kafka.configuration.KafkaBinderConfigurationProperties;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.support.KafkaHeaders;
+import org.springframework.kafka.test.core.BrokerAddress;
+import org.springframework.kafka.test.rule.KafkaEmbedded;
+import org.springframework.retry.RetryOperations;
+
+/**
+ * Integration tests for the {@link KafkaMessageChannelBinder}.
+ *
+ * @author Eric Bottard
+ * @author Marius Bogoevici
+ * @author Mark Fisher
+ * @author Ilayaperumal Gopinathan
+ */
+public class Kafka10BinderTests extends KafkaBinderTests {
+
+ private final String CLASS_UNDER_TEST_NAME = KafkaMessageChannelBinder.class.getSimpleName();
+
+ @ClassRule
+ public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, 10);
+
+ private Kafka10TestBinder binder;
+
+ private Kafka10AdminUtilsOperation adminUtilsOperation = new Kafka10AdminUtilsOperation();
+
+ @Override
+ protected void binderBindUnbindLatency() throws InterruptedException {
+ Thread.sleep(500);
+ }
+
+ @Override
+ protected Kafka10TestBinder getBinder() {
+ if (binder == null) {
+ KafkaBinderConfigurationProperties binderConfiguration = createConfigurationProperties();
+ binder = new Kafka10TestBinder(binderConfiguration);
+ }
+ return binder;
+ }
+
+ protected KafkaBinderConfigurationProperties createConfigurationProperties() {
+ KafkaBinderConfigurationProperties binderConfiguration = new KafkaBinderConfigurationProperties();
+ BrokerAddress[] brokerAddresses = embeddedKafka.getBrokerAddresses();
+ List bAddresses = new ArrayList<>();
+ for (BrokerAddress bAddress : brokerAddresses) {
+ bAddresses.add(bAddress.toString());
+ }
+ String[] foo = new String[bAddresses.size()];
+ binderConfiguration.setBrokers(bAddresses.toArray(foo));
+ binderConfiguration.setZkNodes(embeddedKafka.getZookeeperConnectionString());
+ return binderConfiguration;
+ }
+
+ @Override
+ protected int partitionSize(String topic) {
+ return consumerFactory().createConsumer().partitionsFor(topic).size();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void setMetadataRetryOperations(Binder binder, RetryOperations retryOperations) {
+ ((Kafka10TestBinder) binder).getBinder().setMetadataRetryOperations(retryOperations);
+ }
+
+ @Override
+ protected ZkUtils getZkUtils(KafkaBinderConfigurationProperties kafkaBinderConfigurationProperties) {
+ final ZkClient zkClient = new ZkClient(kafkaBinderConfigurationProperties.getZkConnectionString(),
+ kafkaBinderConfigurationProperties.getZkSessionTimeout(), kafkaBinderConfigurationProperties.getZkConnectionTimeout(),
+ ZKStringSerializer$.MODULE$);
+
+ return new ZkUtils(zkClient, null, false);
+ }
+
+ @Override
+ protected void invokeCreateTopic(ZkUtils zkUtils, String topic, int partitions, int replicationFactor, Properties topicConfig) {
+ adminUtilsOperation.invokeCreateTopic(zkUtils, topic, partitions, replicationFactor, new Properties());
+ }
+
+ @Override
+ protected int invokePartitionSize(String topic, ZkUtils zkUtils) {
+ return adminUtilsOperation.partitionSize(topic, zkUtils);
+ }
+
+ @Override
+ protected ExtendedConsumerProperties createConsumerProperties() {
+ return new ExtendedConsumerProperties<>(new KafkaConsumerProperties());
+ }
+
+ @Override
+ protected ExtendedProducerProperties createProducerProperties() {
+ return new ExtendedProducerProperties<>(new KafkaProducerProperties());
+ }
+
+ @Override
+ public String getKafkaOffsetHeaderKey() {
+ return KafkaHeaders.OFFSET;
+ }
+
+ @Override
+ protected Binder getBinder(KafkaBinderConfigurationProperties kafkaBinderConfigurationProperties) {
+ return new Kafka10TestBinder(kafkaBinderConfigurationProperties);
+ }
+
+ @Before
+ public void init() {
+ String multiplier = System.getenv("KAFKA_TIMEOUT_MULTIPLIER");
+ if (multiplier != null) {
+ timeoutMultiplier = Double.parseDouble(multiplier);
+ }
+ }
+
+ @Override
+ protected boolean usesExplicitRouting() {
+ return false;
+ }
+
+ @Override
+ protected String getClassUnderTestName() {
+ return CLASS_UNDER_TEST_NAME;
+ }
+
+ @Override
+ public Spy spyOn(final String name) {
+ throw new UnsupportedOperationException("'spyOn' is not used by Kafka tests");
+ }
+
+
+ private ConsumerFactory consumerFactory() {
+ Map props = new HashMap<>();
+ KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties();
+ props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, configurationProperties.getKafkaConnectionString());
+ props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
+ props.put(ConsumerConfig.GROUP_ID_CONFIG, "TEST-CONSUMER-GROUP");
+ Deserializer valueDecoder = new ByteArrayDeserializer();
+ Deserializer keyDecoder = new ByteArrayDeserializer();
+
+ return new DefaultKafkaConsumerFactory<>(props, keyDecoder, valueDecoder);
+ }
+
+}
diff --git a/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java b/spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10TestBinder.java
similarity index 52%
rename from spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java
rename to spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10TestBinder.java
index e551bd8b0..82329db49 100644
--- a/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java
+++ b/spring-cloud-stream-binder-kafka-0.10-test/src/test/java/org/springframework/cloud/stream/binder/kafka/Kafka10TestBinder.java
@@ -16,20 +16,9 @@
package org.springframework.cloud.stream.binder.kafka;
-import java.util.List;
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Registration;
-
-import org.springframework.cloud.stream.binder.AbstractTestBinder;
-import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
-import org.springframework.cloud.stream.binder.ExtendedProducerProperties;
-import org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfigurationProperties;
+import org.springframework.cloud.stream.binder.kafka.admin.Kafka10AdminUtilsOperation;
+import org.springframework.cloud.stream.binder.kafka.configuration.KafkaBinderConfigurationProperties;
import org.springframework.context.support.GenericApplicationContext;
-import org.springframework.integration.codec.Codec;
-import org.springframework.integration.codec.kryo.KryoRegistrar;
-import org.springframework.integration.codec.kryo.PojoCodec;
-import org.springframework.integration.tuple.TupleKryoRegistrar;
import org.springframework.kafka.support.LoggingProducerListener;
import org.springframework.kafka.support.ProducerListener;
@@ -41,10 +30,9 @@ import org.springframework.kafka.support.ProducerListener;
* @author Gary Russell
* @author Soby Chacko
*/
-public class KafkaTestBinder extends
- AbstractTestBinder, ExtendedProducerProperties> {
+public class Kafka10TestBinder extends AbstractKafkaTestBinder {
- public KafkaTestBinder(KafkaBinderConfigurationProperties binderConfiguration) {
+ public Kafka10TestBinder(KafkaBinderConfigurationProperties binderConfiguration) {
try {
KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(binderConfiguration);
binder.setCodec(getCodec());
@@ -53,6 +41,7 @@ public class KafkaTestBinder extends
GenericApplicationContext context = new GenericApplicationContext();
context.refresh();
binder.setApplicationContext(context);
+ binder.setAdminUtilsOperation(new Kafka10AdminUtilsOperation());
binder.afterPropertiesSet();
this.setBinder(binder);
}
@@ -61,27 +50,4 @@ public class KafkaTestBinder extends
}
}
- @Override
- public void cleanup() {
- // do nothing - the rule will take care of that
- }
-
- private static Codec getCodec() {
- return new PojoCodec(new TupleRegistrar());
- }
-
- private static class TupleRegistrar implements KryoRegistrar {
- private final TupleKryoRegistrar delegate = new TupleKryoRegistrar();
-
- @Override
- public void registerTypes(Kryo kryo) {
- this.delegate.registerTypes(kryo);
- }
-
- @Override
- public List getRegistrations() {
- return this.delegate.getRegistrations();
- }
- }
-
}
diff --git a/spring-cloud-stream-binder-kafka-docs/src/main/asciidoc/overview.adoc b/spring-cloud-stream-binder-kafka-docs/src/main/asciidoc/overview.adoc
index f4ee39838..ef6b8b823 100644
--- a/spring-cloud-stream-binder-kafka-docs/src/main/asciidoc/overview.adoc
+++ b/spring-cloud-stream-binder-kafka-docs/src/main/asciidoc/overview.adoc
@@ -120,6 +120,12 @@ Default: `2097152`.
The following properties are available for Kafka consumers only and
must be prefixed with `spring.cloud.stream.kafka.bindings..consumer.`.
+autoRebalanceEnabled::
+ When this is enabled, topic partitions will be automatically rebalanced between various consumers by the broker.
+ If set to `false`, it will trigger partitions to be statically allocated by the binder.
+When setting false, in order for static allocation of partitions to take place, it also needs both `spring.cloud.stream.instaceCount` and
+'spring.cloud.stream.instanceIndex' properties set appropriately. The property `spring.cloud.stream.instaceCount` must be greater than 1 in this case.
+
autoCommitOffset::
Whether to autocommit offsets when a message has been processed.
If set to `false`, an `Acknowledgment` header will be available in the message headers for late acknowledgment.
@@ -226,4 +232,36 @@ Here is an example of launching a Spring Cloud Stream application with SASL and
Exercise caution when using the `autoCreateTopics` and `autoAddPartitions` if using Kerberos.
Usually applications may use principals that do not have administrative rights in Kafka and Zookeeper, and relying on Spring Cloud Stream to create/modify topics may fail.
In secure environments, we strongly recommend creating topics and managing ACLs administratively using Kafka tooling.
+====
+
+[NOTE]
+====
+In addition to supporting 0.9 based clients, Kafka binder can also work with 0.10 libs. In order to support this, when you create the project that contains your application, include `spring-cloud-starter-stream-kafka` as you normally would do for 0.9 based applications.
+Then add these dependencies at the top of the dependencies section in the pom.xml file.
+
+[source,xml]
+----
+
+ org.springframework.kafka
+ spring-kafka
+ 1.1.0.M1
+
+
+ org.springframework.integration
+ spring-integration-kafka
+ 2.0.1.RELEASE
+
+
+ org.apache.kafka
+ kafka_2.11
+ 0.10.0.0
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+----
+
====
\ No newline at end of file
diff --git a/spring-cloud-stream-binder-kafka-test-support/pom.xml b/spring-cloud-stream-binder-kafka-test-support/pom.xml
deleted file mode 100644
index 452f44f09..000000000
--- a/spring-cloud-stream-binder-kafka-test-support/pom.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.cloud
- spring-cloud-stream-binder-kafka-parent
- 1.1.0.BUILD-SNAPSHOT
-
- spring-cloud-stream-binder-kafka-test-support
- Kafka related test classes
-
-
-
- junit
- junit
- compile
-
-
- org.springframework.kafka
- spring-kafka-test
- ${spring-kafka.version}
-
-
-
diff --git a/spring-cloud-stream-binder-kafka/pom.xml b/spring-cloud-stream-binder-kafka/pom.xml
index fa07654ff..4d943ae71 100644
--- a/spring-cloud-stream-binder-kafka/pom.xml
+++ b/spring-cloud-stream-binder-kafka/pom.xml
@@ -13,6 +13,12 @@
1.1.0.BUILD-SNAPSHOT
+
+ 0.9.0.1
+ 1.0.3.RELEASE
+ 2.0.1.RELEASE
+
+
org.springframework.boot
@@ -37,12 +43,6 @@
spring-cloud-stream-binder-test
test
-
- org.springframework.cloud
- spring-cloud-stream-binder-kafka-test-support
- test
- 1.1.0.BUILD-SNAPSHOT
-
org.springframework.integration
spring-integration-kafka
@@ -58,6 +58,17 @@
org.springframework.kafka
spring-kafka
${spring-kafka.version}
+ true
+
+
+ org.apache.kafka
+ kafka_2.11
+ true
+
+
+ org.apache.kafka
+ kafka-clients
+ true
org.springframework.kafka
@@ -65,14 +76,6 @@
test
${spring-kafka.version}
-
- org.apache.kafka
- kafka_2.11
-
-
- org.apache.kafka
- kafka-clients
-
org.apache.kafka
kafka_2.11
@@ -111,4 +114,21 @@
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+
+ test-jar
+
+
+
+
+
+
diff --git a/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java b/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java
index 64a09ad1a..8e35d5fe3 100644
--- a/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java
+++ b/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java
@@ -29,10 +29,11 @@ import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfigurationProperties;
+import org.springframework.cloud.stream.binder.kafka.configuration.KafkaBinderConfigurationProperties;
/**
* Health indicator for Kafka.
+ *
* @author Ilayaperumal Gopinathan
* @author Marius Bogoevici
*/
@@ -43,7 +44,7 @@ public class KafkaBinderHealthIndicator implements HealthIndicator {
private final KafkaBinderConfigurationProperties configurationProperties;
public KafkaBinderHealthIndicator(KafkaMessageChannelBinder binder,
- KafkaBinderConfigurationProperties configurationProperties) {
+ KafkaBinderConfigurationProperties configurationProperties) {
this.binder = binder;
this.configurationProperties = configurationProperties;
diff --git a/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java b/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java
index 92ac41df1..548a07b7b 100644
--- a/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java
+++ b/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java
@@ -25,8 +25,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.UUID;
-import kafka.admin.AdminUtils;
-import kafka.api.TopicMetadata;
import kafka.common.ErrorMapping;
import kafka.utils.ZkUtils;
import org.apache.kafka.clients.consumer.ConsumerConfig;
@@ -42,7 +40,6 @@ import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.apache.kafka.common.serialization.ByteArraySerializer;
import org.apache.kafka.common.serialization.Deserializer;
import org.apache.kafka.common.utils.Utils;
-import scala.collection.Seq;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.stream.binder.AbstractMessageChannelBinder;
@@ -52,7 +49,8 @@ import org.springframework.cloud.stream.binder.BinderHeaders;
import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
import org.springframework.cloud.stream.binder.ExtendedProducerProperties;
import org.springframework.cloud.stream.binder.ExtendedPropertiesBinder;
-import org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfigurationProperties;
+import org.springframework.cloud.stream.binder.kafka.admin.AdminUtilsOperation;
+import org.springframework.cloud.stream.binder.kafka.configuration.KafkaBinderConfigurationProperties;
import org.springframework.context.Lifecycle;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -83,6 +81,7 @@ import org.springframework.util.StringUtils;
/**
* A {@link Binder} that uses Kafka as the underlying middleware.
+ *
* @author Eric Bottard
* @author Marius Bogoevici
* @author Ilayaperumal Gopinathan
@@ -109,6 +108,8 @@ public class KafkaMessageChannelBinder extends
private KafkaExtendedBindingProperties extendedBindingProperties = new KafkaExtendedBindingProperties();
+ private AdminUtilsOperation adminUtilsOperation;
+
public KafkaMessageChannelBinder(KafkaBinderConfigurationProperties configurationProperties) {
super(false, headersToMap(configurationProperties));
this.configurationProperties = configurationProperties;
@@ -130,8 +131,13 @@ public class KafkaMessageChannelBinder extends
return headersToMap;
}
+ public void setAdminUtilsOperation(AdminUtilsOperation adminUtilsOperation) {
+ this.adminUtilsOperation = adminUtilsOperation;
+ }
+
/**
* Retry configuration for operations such as validating topic creation
+ *
* @param metadataRetryOperations the retry configuration
*/
public void setMetadataRetryOperations(RetryOperations metadataRetryOperations) {
@@ -189,7 +195,7 @@ public class KafkaMessageChannelBinder extends
@Override
protected MessageHandler createProducerMessageHandler(final String destination,
- ExtendedProducerProperties producerProperties) throws Exception {
+ ExtendedProducerProperties producerProperties) throws Exception {
KafkaTopicUtils.validateTopicName(destination);
@@ -215,11 +221,12 @@ public class KafkaMessageChannelBinder extends
@Override
protected String createProducerDestinationIfNecessary(String name,
- ExtendedProducerProperties properties) {
+ ExtendedProducerProperties properties) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Using kafka topic for outbound: " + name);
}
KafkaTopicUtils.validateTopicName(name);
+
Collection partitions = ensureTopicCreated(name, properties.getPartitionCount());
if (properties.getPartitionCount() < partitions.size()) {
if (this.logger.isInfoEnabled()) {
@@ -259,17 +266,19 @@ public class KafkaMessageChannelBinder extends
@Override
protected Collection createConsumerDestinationIfNecessary(String name, String group,
- ExtendedConsumerProperties properties) {
+ ExtendedConsumerProperties properties) {
KafkaTopicUtils.validateTopicName(name);
if (properties.getInstanceCount() == 0) {
throw new IllegalArgumentException("Instance count cannot be zero");
}
+
Collection allPartitions = ensureTopicCreated(name,
properties.getInstanceCount() * properties.getConcurrency());
Collection listenedPartitions;
- if (properties.getInstanceCount() == 1) {
+ if (properties.getExtension().isAutoRebalanceEnabled() ||
+ properties.getInstanceCount() == 1) {
listenedPartitions = allPartitions;
}
else {
@@ -288,7 +297,7 @@ public class KafkaMessageChannelBinder extends
@Override
@SuppressWarnings("unchecked")
protected MessageProducer createConsumerEndpoint(String name, String group, Collection destination,
- ExtendedConsumerProperties properties) {
+ ExtendedConsumerProperties properties) {
boolean anonymous = !StringUtils.hasText(group);
Assert.isTrue(!anonymous || !properties.getExtension().isEnableDlq(),
"DLQ support is not available for anonymous subscriptions");
@@ -429,38 +438,38 @@ public class KafkaMessageChannelBinder extends
this.configurationProperties.getZkConnectionTimeout(),
JaasUtils.isZkSecurityEnabled());
try {
- final Properties topicConfig = new Properties();
- TopicMetadata topicMetadata = AdminUtils.fetchTopicMetadataFromZk(topicName, zkUtils);
- if (topicMetadata.errorCode() == ErrorMapping.NoError()) {
- // only consider minPartitionCount for resizing if autoAddPartitions is
- // true
+ short errorCode = adminUtilsOperation.errorCodeFromTopicMetadata(topicName, zkUtils);
+ if (errorCode == ErrorMapping.NoError()) {
+ // only consider minPartitionCount for resizing if autoAddPartitions is true
int effectivePartitionCount = this.configurationProperties.isAutoAddPartitions()
? Math.max(this.configurationProperties.getMinPartitionCount(), partitionCount)
: partitionCount;
- if (topicMetadata.partitionsMetadata().size() < effectivePartitionCount) {
+ int partitionSize = adminUtilsOperation.partitionSize(topicName, zkUtils);
+
+ if (partitionSize < effectivePartitionCount) {
if (this.configurationProperties.isAutoAddPartitions()) {
- AdminUtils.addPartitions(zkUtils, topicName, effectivePartitionCount, null, false);
+ adminUtilsOperation.invokeAddPartitions(zkUtils, topicName, effectivePartitionCount, null, false);
}
else {
- int topicSize = topicMetadata.partitionsMetadata().size();
throw new BinderException("The number of expected partitions was: " + partitionCount + ", but "
- + topicSize + (topicSize > 1 ? " have " : " has ") + "been found instead."
+ + partitionSize + (partitionSize > 1 ? " have " : " has ") + "been found instead."
+ "Consider either increasing the partition count of the topic or enabling " +
"`autoAddPartitions`");
}
}
}
- else if (topicMetadata.errorCode() == ErrorMapping.UnknownTopicOrPartitionCode()) {
+ else if (errorCode == ErrorMapping.UnknownTopicOrPartitionCode()) {
if (this.configurationProperties.isAutoCreateTopics()) {
- Seq