From 97f4b0f5f4c94844fbfe55835ab9a690e504a549 Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Thu, 2 Jun 2016 18:22:37 +0200 Subject: [PATCH] Move binders out of the main repo Fixes spring-cloud/spring-cloud-stream#546 Fixing merge conflicts --- pom.xml | 2 +- .../pom.xml | 2 +- .../stream/binder/AbstractBinderTests.java | 0 .../stream/binder/AbstractTestBinder.java | 0 .../cloud/stream/binder/BinderTestUtils.java | 0 .../stream/binder/BrokerBinderTests.java | 0 .../binder/PartitionCapableBinderTests.java | 0 .../stream/binder/PartitionTestSupport.java | 0 .../cloud/stream/binder/Spy.java | 0 .../cloud/stream/binder/TestUtils.java | 0 .../src/main/resources/logback.xml | 0 .../MessageChannelBinderSupportTests.java | 0 spring-cloud-stream-binders/pom.xml | 31 - .../spring-cloud-stream-binder-kafka/pom.xml | 143 --- .../kafka/KafkaBinderHealthIndicator.java | 95 -- .../binder/kafka/KafkaBindingProperties.java | 43 - .../binder/kafka/KafkaConsumerProperties.java | 83 -- .../kafka/KafkaExtendedBindingProperties.java | 60 -- .../kafka/KafkaMessageChannelBinder.java | 800 ----------------- .../binder/kafka/KafkaProducerProperties.java | 68 -- .../binder/kafka/WindowingOffsetManager.java | 262 ------ .../config/KafkaBinderConfiguration.java | 79 -- .../KafkaBinderConfigurationProperties.java | 243 ----- .../stream/binder/kafka/package-info.java | 20 - .../main/resources/META-INF/spring.binders | 2 - .../stream/binder/kafka/KafkaBinderTests.java | 808 ----------------- .../stream/binder/kafka/KafkaTestBinder.java | 89 -- .../kafka/RawKafkaPartitionTestSupport.java | 38 - .../binder/kafka/RawModeKafkaBinderTests.java | 262 ------ .../spring-cloud-stream-binder-rabbit/pom.xml | 59 -- .../binder/rabbit/RabbitAdminException.java | 36 - .../binder/rabbit/RabbitBindingCleaner.java | 195 ---- .../rabbit/RabbitBindingProperties.java | 43 - .../rabbit/RabbitConsumerProperties.java | 162 ---- .../RabbitExtendedBindingProperties.java | 60 -- .../binder/rabbit/RabbitManagementUtils.java | 83 -- .../rabbit/RabbitMessageChannelBinder.java | 651 -------------- .../rabbit/RabbitProducerProperties.java | 142 --- .../RabbitBinderConfigurationProperties.java | 66 -- ...bbitMessageChannelBinderConfiguration.java | 86 -- .../RabbitServiceAutoConfiguration.java | 137 --- .../stream/binder/rabbit/package-info.java | 21 - .../main/resources/META-INF/spring.binders | 2 - ...ueueConnectionFactoryIntegrationTests.java | 73 -- .../rabbit/RabbitBinderCleanerTests.java | 210 ----- .../binder/rabbit/RabbitBinderTests.java | 843 ------------------ .../binder/rabbit/RabbitTestBinder.java | 107 --- .../integration/RabbitBinderModuleTests.java | 210 ----- .../src/test/resources/log4j.properties | 8 - 49 files changed, 2 insertions(+), 6322 deletions(-) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/pom.xml (95%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/AbstractBinderTests.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/AbstractTestBinder.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/BinderTestUtils.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/BrokerBinderTests.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/PartitionCapableBinderTests.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/PartitionTestSupport.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/Spy.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/java/org/springframework/cloud/stream/binder/TestUtils.java (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/main/resources/logback.xml (100%) rename {spring-cloud-stream-binders/spring-cloud-stream-binder-test => spring-cloud-stream-binder-test}/src/test/java/org/springframework/cloud/stream/binder/MessageChannelBinderSupportTests.java (100%) delete mode 100644 spring-cloud-stream-binders/pom.xml delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/pom.xml delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBindingProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaConsumerProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaExtendedBindingProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaProducerProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/WindowingOffsetManager.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfiguration.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfigurationProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/package-info.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/resources/META-INF/spring.binders delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawKafkaPartitionTestSupport.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawModeKafkaBinderTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/pom.xml delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitAdminException.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingCleaner.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitConsumerProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitExtendedBindingProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitManagementUtils.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitMessageChannelBinder.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitProducerProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitBinderConfigurationProperties.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitMessageChannelBinderConfiguration.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/package-info.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/resources/META-INF/spring.binders delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/LocalizedQueueConnectionFactoryIntegrationTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderCleanerTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitTestBinder.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java delete mode 100644 spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/resources/log4j.properties diff --git a/pom.xml b/pom.xml index 04d3d0253..eba2e8d25 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ spring-cloud-stream-dependencies spring-cloud-stream - spring-cloud-stream-binders + spring-cloud-stream-binder-test spring-cloud-stream-codec spring-cloud-stream-starters spring-cloud-stream-rxjava diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/pom.xml b/spring-cloud-stream-binder-test/pom.xml similarity index 95% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/pom.xml rename to spring-cloud-stream-binder-test/pom.xml index 64958b257..1d5328a20 100644 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/pom.xml +++ b/spring-cloud-stream-binder-test/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud - spring-cloud-stream-binders-parent + spring-cloud-stream-parent 1.1.0.BUILD-SNAPSHOT diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractBinderTests.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractBinderTests.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractBinderTests.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractBinderTests.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractTestBinder.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractTestBinder.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractTestBinder.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/AbstractTestBinder.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BinderTestUtils.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BinderTestUtils.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BinderTestUtils.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BinderTestUtils.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BrokerBinderTests.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BrokerBinderTests.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BrokerBinderTests.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/BrokerBinderTests.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionCapableBinderTests.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionCapableBinderTests.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionCapableBinderTests.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionCapableBinderTests.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionTestSupport.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionTestSupport.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionTestSupport.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/PartitionTestSupport.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/Spy.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/Spy.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/Spy.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/Spy.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/TestUtils.java b/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/TestUtils.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/TestUtils.java rename to spring-cloud-stream-binder-test/src/main/java/org/springframework/cloud/stream/binder/TestUtils.java diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/resources/logback.xml b/spring-cloud-stream-binder-test/src/main/resources/logback.xml similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/main/resources/logback.xml rename to spring-cloud-stream-binder-test/src/main/resources/logback.xml diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/test/java/org/springframework/cloud/stream/binder/MessageChannelBinderSupportTests.java b/spring-cloud-stream-binder-test/src/test/java/org/springframework/cloud/stream/binder/MessageChannelBinderSupportTests.java similarity index 100% rename from spring-cloud-stream-binders/spring-cloud-stream-binder-test/src/test/java/org/springframework/cloud/stream/binder/MessageChannelBinderSupportTests.java rename to spring-cloud-stream-binder-test/src/test/java/org/springframework/cloud/stream/binder/MessageChannelBinderSupportTests.java diff --git a/spring-cloud-stream-binders/pom.xml b/spring-cloud-stream-binders/pom.xml deleted file mode 100644 index 6c82410ce..000000000 --- a/spring-cloud-stream-binders/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - 4.0.0 - spring-cloud-stream-binders-parent - pom - https://github.com/spring-cloud/spring-cloud-stream - - Pivotal Software, Inc. - http://www.spring.io - - - - org.springframework.cloud - spring-cloud-stream-parent - 1.1.0.BUILD-SNAPSHOT - - - - spring-cloud-stream-binder-kafka - spring-cloud-stream-binder-rabbit - spring-cloud-stream-binder-test - - - - - org.springframework.boot - spring-boot-starter-logging - - - diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/pom.xml b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/pom.xml deleted file mode 100644 index 28a5a76e5..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/pom.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - 4.0.0 - - spring-cloud-stream-binder-kafka - jar - spring-cloud-stream-binder-kafka - Kafka binder implementation - - - org.springframework.cloud - spring-cloud-stream-binders-parent - 1.1.0.BUILD-SNAPSHOT - - - - 0.8.2.2 - 2.6.0 - 1.3.1.RELEASE - 1.0.0 - - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.cloud - spring-cloud-stream - - - org.springframework.cloud - spring-cloud-stream-codec - - - org.springframework.boot - spring-boot-autoconfigure - true - - - org.springframework.cloud - spring-cloud-stream-binder-test - test - - - org.springframework.cloud - spring-cloud-stream-test-support-internal - test - - - org.springframework.integration - spring-integration-kafka - ${spring-integration-kafka.version} - - - org.apache.avro - avro-compiler - - - - - org.apache.kafka - kafka_2.10 - - - org.apache.kafka - kafka-clients - - - io.reactivex - rxjava - - - io.reactivex - rxjava-math - ${rxjava-math.version} - - - org.apache.curator - curator-recipes - test - - - org.apache.kafka - kafka_2.10 - test - test - - - org.apache.curator - curator-test - test - - - - - - - org.apache.kafka - kafka_2.10 - ${kafka.version} - - - org.slf4j - slf4j-log4j12 - - - log4j - log4j - - - - - org.apache.kafka - kafka_2.10 - test - ${kafka.version} - - - org.apache.kafka - kafka-clients - ${kafka.version} - - - org.apache.curator - curator-framework - ${curator.version} - - - org.apache.curator - curator-recipes - ${curator.version} - - - org.apache.curator - curator-test - ${curator.version} - - - - diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java deleted file mode 100644 index c523f0854..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 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.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import kafka.cluster.Broker; -import kafka.utils.ZKStringSerializer$; -import kafka.utils.ZkUtils$; -import org.I0Itec.zkclient.ZkClient; -import scala.collection.JavaConversions; -import scala.collection.Seq; - -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.integration.kafka.core.BrokerAddress; -import org.springframework.integration.kafka.core.Partition; - -/** - * Health indicator for Kafka. - * - * @author Ilayaperumal Gopinathan - */ -public class KafkaBinderHealthIndicator implements HealthIndicator { - - private final KafkaMessageChannelBinder binder; - - private final KafkaBinderConfigurationProperties configurationProperties; - - public KafkaBinderHealthIndicator(KafkaMessageChannelBinder binder, - KafkaBinderConfigurationProperties configurationProperties) { - this.binder = binder; - this.configurationProperties = configurationProperties; - } - - @Override - public Health health() { - ZkClient zkClient = null; - try { - zkClient = new ZkClient(configurationProperties.getZkConnectionString(), - configurationProperties.getZkSessionTimeout(), - configurationProperties.getZkConnectionTimeout(), ZKStringSerializer$.MODULE$); - Set brokersInClusterSet = new HashSet<>(); - Seq allBrokersInCluster = ZkUtils$.MODULE$.getAllBrokersInCluster(zkClient); - Collection brokersInCluster = JavaConversions.asJavaCollection(allBrokersInCluster); - for (Broker broker : brokersInCluster) { - brokersInClusterSet.add(broker.connectionString()); - } - Set downMessages = new HashSet<>(); - for (Map.Entry> entry : binder.getTopicsInUse().entrySet()) { - for (Partition partition : entry.getValue()) { - BrokerAddress address = binder.getConnectionFactory().getLeader(partition); - if (!brokersInClusterSet.contains(address.toString())) { - downMessages.add(address.toString()); - } - } - } - if (downMessages.isEmpty()) { - return Health.up().build(); - } - return Health.down().withDetail("Following brokers are down: ", downMessages.toString()).build(); - } - catch (Exception e) { - return Health.down(e).build(); - } - finally { - if (zkClient != null) { - try { - zkClient.close(); - } - catch (Exception e) { - // ignore - } - } - } - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBindingProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBindingProperties.java deleted file mode 100644 index 6bd233d63..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBindingProperties.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 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; - -/** - * @author Marius Bogoevici - */ -public class KafkaBindingProperties { - - private KafkaConsumerProperties consumer = new KafkaConsumerProperties(); - - private KafkaProducerProperties producer = new KafkaProducerProperties(); - - public KafkaConsumerProperties getConsumer() { - return consumer; - } - - public void setConsumer(KafkaConsumerProperties consumer) { - this.consumer = consumer; - } - - public KafkaProducerProperties getProducer() { - return producer; - } - - public void setProducer(KafkaProducerProperties producer) { - this.producer = producer; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaConsumerProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaConsumerProperties.java deleted file mode 100644 index c52a38221..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaConsumerProperties.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 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; - -/** - * @author Marius Bogoevici - */ -public class KafkaConsumerProperties { - - private boolean autoCommitOffset = true; - - private Boolean autoCommitOnError; - - private boolean resetOffsets; - - private KafkaMessageChannelBinder.StartOffset startOffset; - - private boolean enableDlq; - - private int recoveryInterval = 5000; - - public boolean isAutoCommitOffset() { - return autoCommitOffset; - } - - public void setAutoCommitOffset(boolean autoCommitOffset) { - this.autoCommitOffset = autoCommitOffset; - } - - public boolean isResetOffsets() { - return resetOffsets; - } - - public void setResetOffsets(boolean resetOffsets) { - this.resetOffsets = resetOffsets; - } - - public KafkaMessageChannelBinder.StartOffset getStartOffset() { - return startOffset; - } - - public void setStartOffset(KafkaMessageChannelBinder.StartOffset startOffset) { - this.startOffset = startOffset; - } - - public boolean isEnableDlq() { - return enableDlq; - } - - public void setEnableDlq(boolean enableDlq) { - this.enableDlq = enableDlq; - } - - public Boolean getAutoCommitOnError() { - return autoCommitOnError; - } - - public void setAutoCommitOnError(Boolean autoCommitOnError) { - this.autoCommitOnError = autoCommitOnError; - } - - public int getRecoveryInterval() { - return recoveryInterval; - } - - public void setRecoveryInterval(int recoveryInterval) { - this.recoveryInterval = recoveryInterval; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaExtendedBindingProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaExtendedBindingProperties.java deleted file mode 100644 index 637d14a35..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaExtendedBindingProperties.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 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.HashMap; -import java.util.Map; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.stream.binder.ExtendedBindingProperties; - -/** - * @author Marius Bogoevici - */ -@ConfigurationProperties("spring.cloud.stream.kafka") -public class KafkaExtendedBindingProperties implements ExtendedBindingProperties { - - private Map bindings = new HashMap<>(); - - public Map getBindings() { - return bindings; - } - - public void setBindings(Map bindings) { - this.bindings = bindings; - } - - @Override - public KafkaConsumerProperties getExtendedConsumerProperties(String channelName) { - if (bindings.containsKey(channelName) && bindings.get(channelName).getConsumer() != null) { - return bindings.get(channelName).getConsumer(); - } - else { - return new KafkaConsumerProperties(); - } - } - - @Override - public KafkaProducerProperties getExtendedProducerProperties(String channelName) { - if (bindings.containsKey(channelName) && bindings.get(channelName).getProducer() != null) { - return bindings.get(channelName).getProducer(); - } - else { - return new KafkaProducerProperties(); - } - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java deleted file mode 100644 index 359fd4d5c..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaMessageChannelBinder.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * 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.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -import kafka.admin.AdminUtils; -import kafka.api.OffsetRequest; -import kafka.api.TopicMetadata; -import kafka.common.ErrorMapping; -import kafka.serializer.Decoder; -import kafka.serializer.DefaultDecoder; -import kafka.utils.ZKStringSerializer$; -import kafka.utils.ZkUtils; -import org.I0Itec.zkclient.ZkClient; -import org.apache.kafka.clients.producer.Callback; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.serialization.ByteArraySerializer; -import org.apache.kafka.common.utils.Utils; -import scala.collection.Seq; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.cloud.stream.binder.AbstractBinder; -import org.springframework.cloud.stream.binder.Binder; -import org.springframework.cloud.stream.binder.BinderException; -import org.springframework.cloud.stream.binder.BinderHeaders; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.DefaultBinding; -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.HeaderMode; -import org.springframework.cloud.stream.binder.MessageValues; -import org.springframework.cloud.stream.binder.PartitionHandler; -import org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfigurationProperties; -import org.springframework.http.MediaType; -import org.springframework.integration.channel.FixedSubscriberChannel; -import org.springframework.integration.endpoint.EventDrivenConsumer; -import org.springframework.integration.handler.AbstractMessageHandler; -import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; -import org.springframework.integration.kafka.core.ConnectionFactory; -import org.springframework.integration.kafka.core.DefaultConnectionFactory; -import org.springframework.integration.kafka.core.KafkaMessage; -import org.springframework.integration.kafka.core.Partition; -import org.springframework.integration.kafka.core.ZookeeperConfiguration; -import org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter; -import org.springframework.integration.kafka.listener.AcknowledgingMessageListener; -import org.springframework.integration.kafka.listener.Acknowledgment; -import org.springframework.integration.kafka.listener.ErrorHandler; -import org.springframework.integration.kafka.listener.KafkaMessageListenerContainer; -import org.springframework.integration.kafka.listener.KafkaNativeOffsetManager; -import org.springframework.integration.kafka.listener.MessageListener; -import org.springframework.integration.kafka.listener.OffsetManager; -import org.springframework.integration.kafka.support.KafkaHeaders; -import org.springframework.integration.kafka.support.ProducerConfiguration; -import org.springframework.integration.kafka.support.ProducerFactoryBean; -import org.springframework.integration.kafka.support.ProducerListener; -import org.springframework.integration.kafka.support.ProducerMetadata; -import org.springframework.integration.kafka.support.ZookeeperConnect; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.SubscribableChannel; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.retry.RetryCallback; -import org.springframework.retry.RetryContext; -import org.springframework.retry.RetryOperations; -import org.springframework.retry.backoff.ExponentialBackOffPolicy; -import org.springframework.retry.policy.SimpleRetryPolicy; -import org.springframework.retry.support.RetryTemplate; -import org.springframework.scheduling.concurrent.CustomizableThreadFactory; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -/** - * A {@link Binder} that uses Kafka as the underlying middleware. - * - * @author Eric Bottard - * @author Marius Bogoevici - * @author Ilayaperumal Gopinathan - * @author David Turanski - * @author Gary Russell - * @author Mark Fisher - * @author Soby Chacko - */ -public class KafkaMessageChannelBinder extends - AbstractBinder, ExtendedProducerProperties> - implements ExtendedPropertiesBinder, - DisposableBean { - - public static final ByteArraySerializer BYTE_ARRAY_SERIALIZER = new ByteArraySerializer(); - - public static final ThreadFactory DAEMON_THREAD_FACTORY; - - static { - CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("kafka-binder-"); - threadFactory.setDaemon(true); - DAEMON_THREAD_FACTORY = threadFactory; - } - - private final KafkaBinderConfigurationProperties configurationProperties; - - private final String[] headersToMap; - - private RetryOperations metadataRetryOperations; - - private final Map> topicsInUse = new HashMap<>(); - - // -------- Default values for properties ------- - - private ConnectionFactory connectionFactory; - - private ProducerListener producerListener; - - private volatile Producer dlqProducer; - - private KafkaExtendedBindingProperties extendedBindingProperties = new KafkaExtendedBindingProperties(); - - public KafkaMessageChannelBinder(KafkaBinderConfigurationProperties configurationProperties) { - this.configurationProperties = configurationProperties; - String[] configuredHeaders = configurationProperties.getHeaders(); - if (ObjectUtils.isEmpty(configuredHeaders)) { - this.headersToMap = BinderHeaders.STANDARD_HEADERS; - } - else { - String[] combinedHeadersToMap = Arrays.copyOfRange(BinderHeaders.STANDARD_HEADERS, 0, - BinderHeaders.STANDARD_HEADERS.length + configuredHeaders.length); - System.arraycopy(configuredHeaders, 0, combinedHeadersToMap, BinderHeaders.STANDARD_HEADERS.length, - configuredHeaders.length); - this.headersToMap = combinedHeadersToMap; - } - } - - String getZkAddress() { - return this.configurationProperties.getZkConnectionString(); - } - - public ConnectionFactory getConnectionFactory() { - return connectionFactory; - } - - public void setProducerListener(ProducerListener producerListener) { - this.producerListener = producerListener; - } - - /** - * Retry configuration for operations such as validating topic creation - * @param metadataRetryOperations the retry configuration - */ - public void setMetadataRetryOperations(RetryOperations metadataRetryOperations) { - this.metadataRetryOperations = metadataRetryOperations; - } - - public void setExtendedBindingProperties(KafkaExtendedBindingProperties extendedBindingProperties) { - this.extendedBindingProperties = extendedBindingProperties; - } - - @Override - public void onInit() throws Exception { - ZookeeperConfiguration configuration = new ZookeeperConfiguration( - new ZookeeperConnect(configurationProperties.getZkConnectionString())); - configuration.setBufferSize(configurationProperties.getSocketBufferSize()); - configuration.setMaxWait(configurationProperties.getMaxWait()); - DefaultConnectionFactory defaultConnectionFactory = new DefaultConnectionFactory(configuration); - defaultConnectionFactory.afterPropertiesSet(); - this.connectionFactory = defaultConnectionFactory; - if (metadataRetryOperations == null) { - RetryTemplate retryTemplate = new RetryTemplate(); - - SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(); - simpleRetryPolicy.setMaxAttempts(10); - retryTemplate.setRetryPolicy(simpleRetryPolicy); - - ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); - backOffPolicy.setInitialInterval(100); - backOffPolicy.setMultiplier(2); - backOffPolicy.setMaxInterval(1000); - retryTemplate.setBackOffPolicy(backOffPolicy); - metadataRetryOperations = retryTemplate; - } - } - - @Override - public void destroy() throws Exception { - if (dlqProducer != null) { - dlqProducer.close(); - dlqProducer = null; - } - } - - /** - * Allowed chars are ASCII alphanumerics, '.', '_' and '-'. - */ - public static void validateTopicName(String topicName) { - try { - byte[] utf8 = topicName.getBytes("UTF-8"); - for (byte b : utf8) { - if (!((b >= 'a') && (b <= 'z') || (b >= 'A') && (b <= 'Z') || (b >= '0') && (b <= '9') || (b == '.') - || (b == '-') || (b == '_'))) { - throw new IllegalArgumentException( - "Topic name can only have ASCII alphanumerics, '.', '_' and '-'"); - } - } - } - catch (UnsupportedEncodingException e) { - throw new AssertionError(e); // Can't happen - } - } - - @Override - public KafkaConsumerProperties getExtendedConsumerProperties(String channelName) { - return extendedBindingProperties.getExtendedConsumerProperties(channelName); - } - - @Override - public KafkaProducerProperties getExtendedProducerProperties(String channelName) { - return extendedBindingProperties.getExtendedProducerProperties(channelName); - } - - Map> getTopicsInUse() { - return this.topicsInUse; - } - - @Override - protected Binding doBindConsumer(String name, String group, MessageChannel inputChannel, - ExtendedConsumerProperties properties) { - // If the caller provides a consumer group, use it; otherwise an anonymous - // consumer group - // is generated each time, such that each anonymous binding will receive all - // messages. - // Consumers reset offsets at the latest time by default, which allows them to - // receive only - // messages sent after they've been bound. That behavior can be changed with the - // "resetOffsets" and "startOffset" properties. - boolean anonymous = !StringUtils.hasText(group); - Assert.isTrue(!anonymous || !properties.getExtension().isEnableDlq(), - "DLQ support is not available for anonymous subscriptions"); - String consumerGroup = anonymous ? "anonymous." + UUID.randomUUID().toString() : group; - // The reference point, if not set explicitly is the latest time for anonymous - // subscriptions and the - // earliest time for group subscriptions. This allows the latter to receive - // messages published before the group - // has been created. - long referencePoint = properties.getExtension().getStartOffset() != null - ? properties.getExtension().getStartOffset().getReferencePoint() - : (anonymous ? OffsetRequest.LatestTime() : OffsetRequest.EarliestTime()); - return createKafkaConsumer(name, inputChannel, properties, consumerGroup, referencePoint); - } - - @Override - public Binding doBindProducer(String name, MessageChannel moduleOutputChannel, - ExtendedProducerProperties properties) { - - Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel); - if (logger.isInfoEnabled()) { - logger.info("Using kafka topic for outbound: " + name); - } - - validateTopicName(name); - - Collection partitions = ensureTopicCreated(name, properties.getPartitionCount()); - - if (properties.getPartitionCount() < partitions.size()) { - if (logger.isInfoEnabled()) { - logger.info("The `partitionCount` of the producer for topic " + name + " is " - + properties.getPartitionCount() + ", smaller than the actual partition count of " - + partitions.size() + " of the topic. The larger number will be used instead."); - } - } - - topicsInUse.put(name, partitions); - - ProducerMetadata producerMetadata = new ProducerMetadata<>(name, byte[].class, byte[].class, - BYTE_ARRAY_SERIALIZER, BYTE_ARRAY_SERIALIZER); - producerMetadata.setSync(properties.getExtension().isSync()); - producerMetadata.setCompressionType(properties.getExtension().getCompressionType()); - producerMetadata.setBatchBytes(properties.getExtension().getBufferSize()); - Properties additionalProps = new Properties(); - additionalProps.put(ProducerConfig.ACKS_CONFIG, String.valueOf(configurationProperties.getRequiredAcks())); - additionalProps.put(ProducerConfig.LINGER_MS_CONFIG, - String.valueOf(properties.getExtension().getBatchTimeout())); - ProducerFactoryBean producerFB = new ProducerFactoryBean<>(producerMetadata, - configurationProperties.getKafkaConnectionString(), additionalProps); - - try { - final ProducerConfiguration producerConfiguration = new ProducerConfiguration<>( - producerMetadata, producerFB.getObject()); - producerConfiguration.setProducerListener(producerListener); - - MessageHandler handler = new SendingHandler(name, properties, partitions.size(), producerConfiguration); - EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel, handler) { - - @Override - protected void doStop() { - super.doStop(); - producerConfiguration.stop(); - } - }; - consumer.setBeanFactory(this.getBeanFactory()); - consumer.setBeanName("outbound." + name); - consumer.afterPropertiesSet(); - DefaultBinding producerBinding = new DefaultBinding<>(name, null, moduleOutputChannel, - consumer); - consumer.start(); - return producerBinding; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Creates a Kafka topic if needed, or try to increase its partition count to the - * desired number. - */ - private Collection ensureTopicCreated(final String topicName, final int partitionCount) { - - final ZkClient zkClient = new ZkClient(configurationProperties.getZkConnectionString(), - configurationProperties.getZkSessionTimeout(), configurationProperties.getZkConnectionTimeout(), - ZKStringSerializer$.MODULE$); - try { - final Properties topicConfig = new Properties(); - TopicMetadata topicMetadata = AdminUtils.fetchTopicMetadataFromZk(topicName, zkClient); - if (topicMetadata.errorCode() == ErrorMapping.NoError()) { - // only consider minPartitionCount for resizing if autoAddPartitions is - // true - int effectivePartitionCount = configurationProperties.isAutoAddPartitions() - ? Math.max(configurationProperties.getMinPartitionCount(), partitionCount) : partitionCount; - if (topicMetadata.partitionsMetadata().size() < effectivePartitionCount) { - if (configurationProperties.isAutoAddPartitions()) { - AdminUtils.addPartitions(zkClient, topicName, effectivePartitionCount, null, false, - new Properties()); - } - 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." - + "Consider either increasing the partition count of the topic or enabling `autoAddPartitions`"); - } - } - } - else if (topicMetadata.errorCode() == ErrorMapping.UnknownTopicOrPartitionCode()) { - if (configurationProperties.isAutoCreateTopics()) { - Seq brokerList = ZkUtils.getSortedBrokerList(zkClient); - // always consider minPartitionCount for topic creation - int effectivePartitionCount = Math.max(configurationProperties.getMinPartitionCount(), - partitionCount); - final scala.collection.Map> replicaAssignment = AdminUtils - .assignReplicasToBrokers(brokerList, effectivePartitionCount, - configurationProperties.getReplicationFactor(), -1, -1); - metadataRetryOperations.execute(new RetryCallback() { - @Override - public Object doWithRetry(RetryContext context) throws RuntimeException { - AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK(zkClient, topicName, - replicaAssignment, topicConfig, true); - return null; - } - }); - } - else { - throw new BinderException("Topic " + topicName + " does not exist"); - } - } - else { - throw new BinderException("Error fetching Kafka topic metadata: ", - ErrorMapping.exceptionFor(topicMetadata.errorCode())); - } - try { - Collection partitions = metadataRetryOperations - .execute(new RetryCallback, Exception>() { - - @Override - public Collection doWithRetry(RetryContext context) throws Exception { - connectionFactory.refreshMetadata(Collections.singleton(topicName)); - Collection partitions = connectionFactory.getPartitions(topicName); - // do a sanity check on the partition set - if (partitions.size() < partitionCount) { - throw new IllegalStateException("The number of expected partitions was: " - + partitionCount + ", but " + partitions.size() - + (partitions.size() > 1 ? " have " : " has ") + "been found instead"); - } - connectionFactory.getLeaders(partitions); - return partitions; - } - }); - return partitions; - } - catch (Exception e) { - logger.error("Cannot initialize Binder", e); - throw new BinderException("Cannot initialize binder:", e); - } - - } - finally { - zkClient.close(); - } - } - - private Binding createKafkaConsumer(String name, final MessageChannel moduleInputChannel, - ExtendedConsumerProperties properties, String group, long referencePoint) { - - validateTopicName(name); - if (properties.getInstanceCount() == 0) { - throw new IllegalArgumentException("Instance count cannot be zero"); - } - Collection allPartitions = ensureTopicCreated(name, - properties.getInstanceCount() * properties.getConcurrency()); - - Decoder valueDecoder = new DefaultDecoder(null); - Decoder keyDecoder = new DefaultDecoder(null); - - Collection listenedPartitions; - - if (properties.getInstanceCount() == 1) { - listenedPartitions = allPartitions; - } - else { - listenedPartitions = new ArrayList<>(); - for (Partition partition : allPartitions) { - // divide partitions across modules - if ((partition.getId() % properties.getInstanceCount()) == properties.getInstanceIndex()) { - listenedPartitions.add(partition); - } - } - } - topicsInUse.put(name, listenedPartitions); - ReceivingHandler rh = new ReceivingHandler(properties); - rh.setOutputChannel(moduleInputChannel); - - final FixedSubscriberChannel bridge = new FixedSubscriberChannel(rh); - bridge.setBeanName("bridge." + name); - - Assert.isTrue(!CollectionUtils.isEmpty(listenedPartitions), "A list of partitions must be provided"); - final KafkaMessageListenerContainer messageListenerContainer = new KafkaMessageListenerContainer( - connectionFactory, listenedPartitions.toArray(new Partition[listenedPartitions.size()])); - - if (logger.isDebugEnabled()) { - logger.debug("Listened partitions: " + StringUtils.collectionToCommaDelimitedString(listenedPartitions)); - } - - OffsetManager offsetManager = createOffsetManager(group, referencePoint); - if (properties.getExtension().isResetOffsets()) { - offsetManager.resetOffsets(listenedPartitions); - } - messageListenerContainer.setOffsetManager(offsetManager); - messageListenerContainer.setQueueSize(configurationProperties.getQueueSize()); - messageListenerContainer.setMaxFetch(configurationProperties.getFetchSize()); - boolean autoCommitOnError = properties.getExtension().getAutoCommitOnError() != null - ? properties.getExtension().getAutoCommitOnError() - : properties.getExtension().isAutoCommitOffset() && properties.getExtension().isEnableDlq(); - messageListenerContainer.setAutoCommitOnError(autoCommitOnError); - messageListenerContainer.setRecoveryInterval(properties.getExtension().getRecoveryInterval()); - - int concurrency = Math.min(properties.getConcurrency(), listenedPartitions.size()); - messageListenerContainer.setConcurrency(concurrency); - final ExecutorService dispatcherTaskExecutor = Executors.newFixedThreadPool(concurrency, DAEMON_THREAD_FACTORY); - messageListenerContainer.setDispatcherTaskExecutor(dispatcherTaskExecutor); - final KafkaMessageDrivenChannelAdapter kafkaMessageDrivenChannelAdapter = new KafkaMessageDrivenChannelAdapter( - messageListenerContainer); - kafkaMessageDrivenChannelAdapter.setBeanFactory(this.getBeanFactory()); - kafkaMessageDrivenChannelAdapter.setKeyDecoder(keyDecoder); - kafkaMessageDrivenChannelAdapter.setPayloadDecoder(valueDecoder); - kafkaMessageDrivenChannelAdapter.setOutputChannel(bridge); - kafkaMessageDrivenChannelAdapter.setAutoCommitOffset(properties.getExtension().isAutoCommitOffset()); - kafkaMessageDrivenChannelAdapter.afterPropertiesSet(); - - // we need to wrap the adapter listener into a retrying listener so that the retry - // logic is applied before the ErrorHandler is executed - final RetryTemplate retryTemplate = buildRetryTemplateIfRetryEnabled(properties); - if (retryTemplate != null) { - if (properties.getExtension().isAutoCommitOffset()) { - final MessageListener originalMessageListener = (MessageListener) messageListenerContainer - .getMessageListener(); - messageListenerContainer.setMessageListener(new MessageListener() { - @Override - public void onMessage(final KafkaMessage message) { - try { - retryTemplate.execute(new RetryCallback() { - @Override - public Object doWithRetry(RetryContext context) { - originalMessageListener.onMessage(message); - return null; - } - }); - } - catch (Throwable throwable) { - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; - } - else { - throw new RuntimeException(throwable); - } - } - } - }); - } - else { - messageListenerContainer.setMessageListener(new AcknowledgingMessageListener() { - final AcknowledgingMessageListener originalMessageListener = (AcknowledgingMessageListener) messageListenerContainer - .getMessageListener(); - - @Override - public void onMessage(final KafkaMessage message, final Acknowledgment acknowledgment) { - retryTemplate.execute(new RetryCallback() { - @Override - public Object doWithRetry(RetryContext context) { - originalMessageListener.onMessage(message, acknowledgment); - return null; - } - }); - } - }); - } - } - - if (properties.getExtension().isEnableDlq()) { - final String dlqTopic = "error." + name + "." + group; - initDlqProducer(); - messageListenerContainer.setErrorHandler(new ErrorHandler() { - @Override - public void handle(Exception thrownException, final KafkaMessage message) { - final byte[] key = message.getMessage().key() != null ? Utils.toArray(message.getMessage().key()) - : null; - final byte[] payload = message.getMessage().payload() != null - ? Utils.toArray(message.getMessage().payload()) : null; - dlqProducer.send(new ProducerRecord<>(dlqTopic, key, payload), new Callback() { - @Override - public void onCompletion(RecordMetadata metadata, Exception exception) { - StringBuffer messageLog = new StringBuffer(); - messageLog.append(" a message with key='" - + toDisplayString(ObjectUtils.nullSafeToString(key), 50) + "'"); - messageLog.append(" and payload='" - + toDisplayString(ObjectUtils.nullSafeToString(payload), 50) + "'"); - messageLog.append(" received from " + message.getMetadata().getPartition()); - if (exception != null) { - logger.error("Error sending to DLQ" + messageLog.toString(), exception); - } - else { - if (logger.isDebugEnabled()) { - logger.debug("Sent to DLQ " + messageLog.toString()); - } - } - } - }); - } - }); - } - kafkaMessageDrivenChannelAdapter.start(); - - EventDrivenConsumer edc = new EventDrivenConsumer(bridge, rh) { - - @Override - protected void doStop() { - // stop the offset manager and the channel adapter before unbinding - // this means that the upstream channel adapter has a chance to stop - kafkaMessageDrivenChannelAdapter.stop(); - if (messageListenerContainer.getOffsetManager() instanceof DisposableBean) { - try { - ((DisposableBean) messageListenerContainer.getOffsetManager()).destroy(); - } - catch (Exception e) { - logger.error("Error while closing the offset manager", e); - } - } - super.doStop(); - } - }; - String groupedName = groupedName(name, group); - edc.setBeanName("inbound." + groupedName); - - DefaultBinding consumerBinding = new DefaultBinding(name, group, - moduleInputChannel, edc) { - @Override - protected void afterUnbind() { - dispatcherTaskExecutor.shutdown(); - } - }; - edc.start(); - return consumerBinding; - } - - private synchronized void initDlqProducer() { - try { - if (dlqProducer == null) { - synchronized (this) { - if (dlqProducer == null) { - // we can use the producer defaults as we do not need to tune - // performance - ProducerMetadata producerMetadata = new ProducerMetadata<>("dlqKafkaProducer", - byte[].class, byte[].class, BYTE_ARRAY_SERIALIZER, BYTE_ARRAY_SERIALIZER); - producerMetadata.setSync(false); - producerMetadata.setCompressionType(ProducerMetadata.CompressionType.none); - producerMetadata.setBatchBytes(16384); - Properties additionalProps = new Properties(); - additionalProps.put(ProducerConfig.ACKS_CONFIG, - String.valueOf(configurationProperties.getRequiredAcks())); - additionalProps.put(ProducerConfig.LINGER_MS_CONFIG, String.valueOf(0)); - ProducerFactoryBean producerFactoryBean = new ProducerFactoryBean<>( - producerMetadata, configurationProperties.getKafkaConnectionString(), additionalProps); - dlqProducer = producerFactoryBean.getObject(); - } - } - } - } - catch (Exception e) { - throw new RuntimeException("Cannot initialize DLQ producer:", e); - } - } - - private OffsetManager createOffsetManager(String group, long referencePoint) { - try { - - KafkaNativeOffsetManager kafkaOffsetManager = new KafkaNativeOffsetManager(connectionFactory, - new ZookeeperConnect(configurationProperties.getZkConnectionString()), - Collections.emptyMap()); - kafkaOffsetManager.setConsumerId(group); - kafkaOffsetManager.setReferenceTimestamp(referencePoint); - kafkaOffsetManager.afterPropertiesSet(); - - WindowingOffsetManager windowingOffsetManager = new WindowingOffsetManager(kafkaOffsetManager); - windowingOffsetManager.setTimespan(configurationProperties.getOffsetUpdateTimeWindow()); - windowingOffsetManager.setCount(configurationProperties.getOffsetUpdateCount()); - windowingOffsetManager.setShutdownTimeout(configurationProperties.getOffsetUpdateShutdownTimeout()); - - windowingOffsetManager.afterPropertiesSet(); - return windowingOffsetManager; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - private String toDisplayString(String original, int maxCharacters) { - if (original.length() <= maxCharacters) { - return original; - } - return original.substring(0, maxCharacters) + "..."; - } - - @Override - public void doManualAck(LinkedList messageHeadersList) { - Iterator iterator = messageHeadersList.iterator(); - while (iterator.hasNext()) { - MessageHeaders headers = iterator.next(); - Acknowledgment acknowledgment = (Acknowledgment) headers.get(KafkaHeaders.ACKNOWLEDGMENT); - Assert.notNull(acknowledgment, - "Acknowledgement shouldn't be null when acknowledging kafka message " + "manually."); - acknowledgment.acknowledge(); - } - } - - private final class ReceivingHandler extends AbstractReplyProducingMessageHandler { - - private final ExtendedConsumerProperties consumerProperties; - - private ReceivingHandler(ExtendedConsumerProperties consumerProperties) { - this.consumerProperties = consumerProperties; - } - - @Override - protected Object handleRequestMessage(Message requestMessage) { - if (HeaderMode.embeddedHeaders.equals(consumerProperties.getHeaderMode())) { - MessageValues messageValues = extractMessageValues(requestMessage); - return MessageBuilder.createMessage(messageValues.getPayload(), new KafkaBinderHeaders(messageValues)); - } - else { - return requestMessage; - } - } - - @Override - protected boolean shouldCopyRequestHeaders() { - // prevent the message from being copied again in superclass - return false; - } - - @SuppressWarnings("serial") - private final class KafkaBinderHeaders extends MessageHeaders { - KafkaBinderHeaders(Map headers) { - super(headers, MessageHeaders.ID_VALUE_NONE, -1L); - } - } - } - - private final class SendingHandler extends AbstractMessageHandler { - - private final AtomicInteger roundRobinCount = new AtomicInteger(); - - private final String topicName; - - private final ExtendedProducerProperties producerProperties; - - private final int numberOfKafkaPartitions; - - private final ProducerConfiguration producerConfiguration; - - private final PartitionHandler partitionHandler; - - private SendingHandler(String topicName, ExtendedProducerProperties properties, - int numberOfPartitions, ProducerConfiguration producerConfiguration) { - this.topicName = topicName; - producerProperties = properties; - this.numberOfKafkaPartitions = numberOfPartitions; - ConfigurableListableBeanFactory beanFactory = KafkaMessageChannelBinder.this.getBeanFactory(); - this.setBeanFactory(beanFactory); - this.producerConfiguration = producerConfiguration; - this.partitionHandler = new PartitionHandler(beanFactory, evaluationContext, partitionSelector, properties); - } - - @Override - protected void handleMessageInternal(Message message) throws Exception { - int targetPartition; - if (producerProperties.isPartitioned()) { - targetPartition = this.partitionHandler.determinePartition(message); - } - else { - targetPartition = roundRobin() % numberOfKafkaPartitions; - } - if (HeaderMode.embeddedHeaders.equals(producerProperties.getHeaderMode())) { - MessageValues transformed = serializePayloadIfNecessary(message); - byte[] messageToSend = embeddedHeadersMessageConverter.embedHeaders(transformed, - KafkaMessageChannelBinder.this.headersToMap); - producerConfiguration.send(topicName, targetPartition, null, messageToSend); - } - else if (HeaderMode.raw.equals(producerProperties.getHeaderMode())) { - Object contentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE); - if (contentType != null && !contentType.equals(MediaType.APPLICATION_OCTET_STREAM_VALUE)) { - logger.error("Raw mode supports only " + MediaType.APPLICATION_OCTET_STREAM_VALUE + " content type" - + message.getPayload().getClass()); - } - if (message.getPayload() instanceof byte[]) { - producerConfiguration.send(topicName, targetPartition, null, (byte[]) message.getPayload()); - } - else { - logger.error("Raw mode supports only byte[] payloads but value sent was of type " - + message.getPayload().getClass()); - } - } - } - - private int roundRobin() { - int result = roundRobinCount.incrementAndGet(); - if (result == Integer.MAX_VALUE) { - roundRobinCount.set(0); - } - return result; - } - - } - - public enum StartOffset { - earliest(OffsetRequest.EarliestTime()), latest(OffsetRequest.LatestTime()); - - private final long referencePoint; - - StartOffset(long referencePoint) { - this.referencePoint = referencePoint; - } - - public long getReferencePoint() { - return referencePoint; - } - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaProducerProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaProducerProperties.java deleted file mode 100644 index 99f2486a1..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaProducerProperties.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 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 javax.validation.constraints.NotNull; - -import org.springframework.integration.kafka.support.ProducerMetadata; - -/** - * @author Marius Bogoevici - */ -public class KafkaProducerProperties { - - private int bufferSize = 16384; - - private ProducerMetadata.CompressionType compressionType = ProducerMetadata.CompressionType.none; - - private boolean sync; - - private int batchTimeout; - - public int getBufferSize() { - return bufferSize; - } - - public void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - } - - @NotNull - public ProducerMetadata.CompressionType getCompressionType() { - return compressionType; - } - - public void setCompressionType(ProducerMetadata.CompressionType compressionType) { - this.compressionType = compressionType; - } - - public boolean isSync() { - return sync; - } - - public void setSync(boolean sync) { - this.sync = sync; - } - - public int getBatchTimeout() { - return batchTimeout; - } - - public void setBatchTimeout(int batchTimeout) { - this.batchTimeout = batchTimeout; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/WindowingOffsetManager.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/WindowingOffsetManager.java deleted file mode 100644 index 44fa9792d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/WindowingOffsetManager.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2015 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.io.IOException; -import java.util.Collection; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import rx.Observable; -import rx.Subscription; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.functions.Func2; -import rx.observables.GroupedObservable; -import rx.observables.MathObservable; -import rx.subjects.PublishSubject; -import rx.subjects.SerializedSubject; -import rx.subjects.Subject; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.integration.kafka.core.Partition; -import org.springframework.integration.kafka.listener.OffsetManager; -import org.springframework.util.Assert; - -/** - * An {@link OffsetManager} that aggregates writes over a time or count window, using an underlying delegate to - * do the actual operations. Its purpose is to reduce the performance impact of writing operations - * wherever this is desirable. - * - * Either a time window or a number of writes can be specified, but not both. - * - * @author Marius Bogoevici - */ -public class WindowingOffsetManager implements OffsetManager, InitializingBean, DisposableBean { - - private final CreatePartitionAndOffsetFunction createPartitionAndOffsetFunction = new CreatePartitionAndOffsetFunction(); - - private final GetOffsetFunction getOffsetFunction = new GetOffsetFunction(); - - private final ComputeMaximumOffsetByPartitionFunction findHighestOffsetInPartitionGroup = new ComputeMaximumOffsetByPartitionFunction(); - - private final GetPartitionFunction getPartition = new GetPartitionFunction(); - - private final FindHighestOffsetsByPartitionFunction findHighestOffsetsByPartition = new FindHighestOffsetsByPartitionFunction(); - - private final DelegateUpdateOffsetAction delegateUpdateOffsetAction = new DelegateUpdateOffsetAction(); - - private final NotifyObservableClosedAction notifyObservableClosed = new NotifyObservableClosedAction(); - - private final OffsetManager delegate; - - private long timespan = 10 * 1000; - - private int count; - - private Subject offsets; - - private Subscription subscription; - - private int shutdownTimeout = 2000; - - private CountDownLatch shutdownLatch; - - public WindowingOffsetManager(OffsetManager offsetManager) { - this.delegate = offsetManager; - } - - /** - * The timespan for aggregating write operations, before invoking the underlying {@link OffsetManager}. - * - * @param timespan duration in milliseconds - */ - public void setTimespan(long timespan) { - Assert.isTrue(timespan >= 0, "Timespan must be a positive value"); - this.timespan = timespan; - } - - /** - * How many writes should be aggregated, before invoking the underlying {@link OffsetManager}. Setting this value - * to 1 effectively disables windowing. - * - * @param count number of writes - */ - public void setCount(int count) { - Assert.isTrue(count >= 0, "Count must be a positive value"); - this.count = count; - } - - /** - * The timeout that {@link #close()} and {@link #destroy()} operations will wait for receving a confirmation that the - * underlying writes have been processed. - * - * @param shutdownTimeout duration in milliseconds - */ - public void setShutdownTimeout(int shutdownTimeout) { - this.shutdownTimeout = shutdownTimeout; - } - - @Override - public void afterPropertiesSet() throws Exception { - Assert.isTrue(timespan > 0 ^ count > 0, "Only one of the timespan or count must be set"); - // create the stream if windowing is set, and count is higher than 1 - if (timespan > 0 || count > 1) { - offsets = new SerializedSubject<>(PublishSubject.create()); - // window by either count or time - Observable> window = - timespan > 0 ? offsets.window(timespan, TimeUnit.MILLISECONDS) : offsets.window(count); - Observable maximumOffsetsByWindow = window - .flatMap(findHighestOffsetsByPartition) - .doOnCompleted(notifyObservableClosed); - subscription = maximumOffsetsByWindow.subscribe(delegateUpdateOffsetAction); - } - else { - offsets = null; - } - } - - @Override - public void destroy() throws Exception { - this.flush(); - this.close(); - if (delegate instanceof DisposableBean) { - ((DisposableBean) delegate).destroy(); - } - } - - @Override - public void updateOffset(Partition partition, long offset) { - if (offsets != null) { - offsets.onNext(new PartitionAndOffset(partition, offset)); - } - else { - delegate.updateOffset(partition, offset); - } - } - - @Override - public long getOffset(Partition partition) { - return delegate.getOffset(partition); - } - - @Override - public void deleteOffset(Partition partition) { - delegate.deleteOffset(partition); - } - - @Override - public void resetOffsets(Collection partition) { - delegate.resetOffsets(partition); - } - - @Override - public void close() throws IOException { - if (offsets != null) { - shutdownLatch = new CountDownLatch(1); - offsets.onCompleted(); - try { - shutdownLatch.await(shutdownTimeout, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - // ignore - } - subscription.unsubscribe(); - } - delegate.close(); - } - - @Override - public void flush() throws IOException { - delegate.flush(); - } - - private final class PartitionAndOffset { - - private final Partition partition; - - private final Long offset; - - private PartitionAndOffset(Partition partition, Long offset) { - this.partition = partition; - this.offset = offset; - } - - public Partition getPartition() { - return partition; - } - - public Long getOffset() { - return offset; - } - } - - private class DelegateUpdateOffsetAction implements Action1 { - @Override - public void call(PartitionAndOffset partitionAndOffset) { - delegate.updateOffset(partitionAndOffset.getPartition(), partitionAndOffset.getOffset()); - } - } - - private class NotifyObservableClosedAction implements Action0 { - @Override - public void call() { - if (shutdownLatch != null) { - shutdownLatch.countDown(); - } - } - } - - private class CreatePartitionAndOffsetFunction implements Func2 { - @Override - public PartitionAndOffset call(Partition partition, Long offset) { - return new PartitionAndOffset(partition, offset); - } - } - - private class GetOffsetFunction implements Func1 { - @Override - public Long call(PartitionAndOffset partitionAndOffset) { - return partitionAndOffset.getOffset(); - } - } - - private class ComputeMaximumOffsetByPartitionFunction implements Func1, Observable> { - @Override - public Observable call(GroupedObservable group) { - return Observable.zip(Observable.just(group.getKey()), - MathObservable.max(group.map(getOffsetFunction)), - createPartitionAndOffsetFunction); - } - } - - private class GetPartitionFunction implements Func1 { - @Override - public Partition call(PartitionAndOffset partitionAndOffset) { - return partitionAndOffset.getPartition(); - } - } - - private class FindHighestOffsetsByPartitionFunction implements Func1, Observable> { - @Override - public Observable call(Observable windowBuffer) { - return windowBuffer.groupBy(getPartition).flatMap(findHighestOffsetInPartitionGroup); - } - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfiguration.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfiguration.java deleted file mode 100644 index 623bbf35d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2015-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.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.stream.binder.Binder; -import org.springframework.cloud.stream.binder.kafka.KafkaBinderHealthIndicator; -import org.springframework.cloud.stream.binder.kafka.KafkaExtendedBindingProperties; -import org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder; -import org.springframework.cloud.stream.config.codec.kryo.KryoCodecAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.integration.codec.Codec; -import org.springframework.integration.kafka.support.LoggingProducerListener; -import org.springframework.integration.kafka.support.ProducerListener; - -/** - * @author David Turanski - * @author Marius Bogoevici - * @author Soby Chacko - * @author Mark Fisher - * @author Ilayaperumal Gopinathan - */ -@Configuration -@ConditionalOnMissingBean(Binder.class) -@Import({ KryoCodecAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) -@EnableConfigurationProperties({ KafkaBinderConfigurationProperties.class, KafkaExtendedBindingProperties.class }) -public class KafkaBinderConfiguration { - - @Autowired - private Codec codec; - - @Autowired - private KafkaBinderConfigurationProperties configurationProperties; - - @Autowired - private KafkaExtendedBindingProperties kafkaExtendedBindingProperties; - - @Autowired - private ProducerListener producerListener; - - @Bean - KafkaMessageChannelBinder kafkaMessageChannelBinder() { - KafkaMessageChannelBinder kafkaMessageChannelBinder = new KafkaMessageChannelBinder(configurationProperties); - kafkaMessageChannelBinder.setCodec(codec); - kafkaMessageChannelBinder.setProducerListener(producerListener); - kafkaMessageChannelBinder.setExtendedBindingProperties(kafkaExtendedBindingProperties); - return kafkaMessageChannelBinder; - } - - @Bean - @ConditionalOnMissingBean(ProducerListener.class) - ProducerListener producerListener() { - return new LoggingProducerListener(); - } - - @Bean - KafkaBinderHealthIndicator healthIndicator(KafkaMessageChannelBinder kafkaMessageChannelBinder) { - return new KafkaBinderHealthIndicator(kafkaMessageChannelBinder, configurationProperties); - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfigurationProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfigurationProperties.java deleted file mode 100644 index 98ee963f2..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/config/KafkaBinderConfigurationProperties.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2015-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.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.StringUtils; - -/** - * @author David Turanski - * @author Ilayaperumal Gopinathan - * @author Marius Bogoevici - */ -@ConfigurationProperties(prefix = "spring.cloud.stream.kafka.binder") -public class KafkaBinderConfigurationProperties { - - private String[] zkNodes = new String[] { "localhost" }; - - private String defaultZkPort = "2181"; - - private String[] brokers = new String[] { "localhost" }; - - private String defaultBrokerPort = "9092"; - - private String[] headers = new String[] {}; - - private int offsetUpdateTimeWindow = 10000; - - private int offsetUpdateCount; - - private int offsetUpdateShutdownTimeout = 2000; - - private int maxWait = 100; - - private boolean autoCreateTopics = true; - - private boolean autoAddPartitions; - - private int socketBufferSize = 2097152; - - /** - * ZK session timeout in milliseconds. - */ - private int zkSessionTimeout = 10000; - - /** - * ZK Connection timeout in milliseconds. - */ - private int zkConnectionTimeout = 10000; - - private int requiredAcks = 1; - - private int replicationFactor = 1; - - private int fetchSize = 1024 * 1024; - - private int minPartitionCount = 1; - - private int queueSize = 8192; - - public String getZkConnectionString() { - return toConnectionString(this.zkNodes, this.defaultZkPort); - } - - public String getKafkaConnectionString() { - return toConnectionString(this.brokers, this.defaultBrokerPort); - } - - public String[] getHeaders() { - return headers; - } - - public int getOffsetUpdateTimeWindow() { - return this.offsetUpdateTimeWindow; - } - - public int getOffsetUpdateCount() { - return this.offsetUpdateCount; - } - - public int getOffsetUpdateShutdownTimeout() { - return this.offsetUpdateShutdownTimeout; - } - - public String[] getZkNodes() { - return zkNodes; - } - - public void setZkNodes(String... zkNodes) { - this.zkNodes = zkNodes; - } - - public void setDefaultZkPort(String defaultZkPort) { - this.defaultZkPort = defaultZkPort; - } - - public String[] getBrokers() { - return brokers; - } - - public void setBrokers(String... brokers) { - this.brokers = brokers; - } - - public void setDefaultBrokerPort(String defaultBrokerPort) { - this.defaultBrokerPort = defaultBrokerPort; - } - - public void setHeaders(String... headers) { - this.headers = headers; - } - - public void setOffsetUpdateTimeWindow(int offsetUpdateTimeWindow) { - this.offsetUpdateTimeWindow = offsetUpdateTimeWindow; - } - - public void setOffsetUpdateCount(int offsetUpdateCount) { - this.offsetUpdateCount = offsetUpdateCount; - } - - public void setOffsetUpdateShutdownTimeout(int offsetUpdateShutdownTimeout) { - this.offsetUpdateShutdownTimeout = offsetUpdateShutdownTimeout; - } - - public int getZkSessionTimeout() { - return this.zkSessionTimeout; - } - - public void setZkSessionTimeout(int zkSessionTimeout) { - this.zkSessionTimeout = zkSessionTimeout; - } - - public int getZkConnectionTimeout() { - return this.zkConnectionTimeout; - } - - public void setZkConnectionTimeout(int zkConnectionTimeout) { - this.zkConnectionTimeout = zkConnectionTimeout; - } - - /** - * Converts an array of host values to a comma-separated String. - * - * It will append the default port value, if not already specified. - */ - private String toConnectionString(String[] hosts, String defaultPort) { - String[] fullyFormattedHosts = new String[hosts.length]; - for (int i = 0; i < hosts.length; i++) { - if (hosts[i].contains(":") || StringUtils.isEmpty(defaultPort)) { - fullyFormattedHosts[i] = hosts[i]; - } - else { - fullyFormattedHosts[i] = hosts[i] + ":" + defaultPort; - } - } - return StringUtils.arrayToCommaDelimitedString(fullyFormattedHosts); - } - - public int getMaxWait() { - return maxWait; - } - - public void setMaxWait(int maxWait) { - this.maxWait = maxWait; - } - - public int getRequiredAcks() { - return requiredAcks; - } - - public void setRequiredAcks(int requiredAcks) { - this.requiredAcks = requiredAcks; - } - - public int getReplicationFactor() { - return replicationFactor; - } - - public void setReplicationFactor(int replicationFactor) { - this.replicationFactor = replicationFactor; - } - - public int getFetchSize() { - return fetchSize; - } - - public void setFetchSize(int fetchSize) { - this.fetchSize = fetchSize; - } - - public int getMinPartitionCount() { - return minPartitionCount; - } - - public void setMinPartitionCount(int minPartitionCount) { - this.minPartitionCount = minPartitionCount; - } - - public int getQueueSize() { - return queueSize; - } - - public void setQueueSize(int queueSize) { - this.queueSize = queueSize; - } - - public boolean isAutoCreateTopics() { - return autoCreateTopics; - } - - public void setAutoCreateTopics(boolean autoCreateTopics) { - this.autoCreateTopics = autoCreateTopics; - } - - public boolean isAutoAddPartitions() { - return autoAddPartitions; - } - - public void setAutoAddPartitions(boolean autoAddPartitions) { - this.autoAddPartitions = autoAddPartitions; - } - - public int getSocketBufferSize() { - return socketBufferSize; - } - - public void setSocketBufferSize(int socketBufferSize) { - this.socketBufferSize = socketBufferSize; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/package-info.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/package-info.java deleted file mode 100644 index a95c7dea1..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2015 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. - */ - -/** - * This package contains an implementation of the {@link org.springframework.cloud.stream.binder.Binder} for Kafka. - */ -package org.springframework.cloud.stream.binder.kafka; diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/resources/META-INF/spring.binders b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/resources/META-INF/spring.binders deleted file mode 100644 index 063a7400f..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/main/resources/META-INF/spring.binders +++ /dev/null @@ -1,2 +0,0 @@ -kafka:\ -org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfiguration diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderTests.java deleted file mode 100644 index 08ca966f1..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderTests.java +++ /dev/null @@ -1,808 +0,0 @@ -/* - * 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.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import kafka.admin.AdminUtils; -import kafka.api.TopicMetadata; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; - -import org.springframework.beans.DirectFieldAccessor; -import org.springframework.cloud.stream.binder.BinderException; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.ExtendedProducerProperties; -import org.springframework.cloud.stream.binder.PartitionCapableBinderTests; -import org.springframework.cloud.stream.binder.Spy; -import org.springframework.cloud.stream.binder.TestUtils; -import org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfigurationProperties; -import org.springframework.cloud.stream.test.junit.kafka.KafkaTestSupport; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.kafka.core.Partition; -import org.springframework.integration.kafka.core.TopicNotFoundException; -import org.springframework.integration.kafka.support.KafkaHeaders; -import org.springframework.integration.kafka.support.ProducerConfiguration; -import org.springframework.integration.kafka.support.ProducerMetadata; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessagingException; -import org.springframework.messaging.support.GenericMessage; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.retry.backoff.FixedBackOffPolicy; -import org.springframework.retry.policy.SimpleRetryPolicy; -import org.springframework.retry.support.RetryTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -/** - * Integration tests for the {@link KafkaMessageChannelBinder}. - * @author Eric Bottard - * @author Marius Bogoevici - * @author Mark Fisher - * @author Ilayaperumal Gopinathan - */ -public class KafkaBinderTests extends - PartitionCapableBinderTests, ExtendedProducerProperties> { - - private final String CLASS_UNDER_TEST_NAME = KafkaMessageChannelBinder.class.getSimpleName(); - - @ClassRule - public static KafkaTestSupport kafkaTestSupport = new KafkaTestSupport(); - - private KafkaTestBinder binder; - - @Override - protected void binderBindUnbindLatency() throws InterruptedException { - Thread.sleep(500); - } - - @Override - protected KafkaTestBinder getBinder() { - if (binder == null) { - KafkaBinderConfigurationProperties binderConfiguration = createConfigurationProperties(); - binder = new KafkaTestBinder(binderConfiguration); - } - return binder; - } - - private KafkaBinderConfigurationProperties createConfigurationProperties() { - KafkaBinderConfigurationProperties binderConfiguration = new KafkaBinderConfigurationProperties(); - binderConfiguration.setBrokers(kafkaTestSupport.getBrokerAddress()); - binderConfiguration.setZkNodes(kafkaTestSupport.getZkConnectString()); - return binderConfiguration; - } - - @Override - protected ExtendedConsumerProperties createConsumerProperties() { - return new ExtendedConsumerProperties<>(new KafkaConsumerProperties()); - } - - @Override - protected ExtendedProducerProperties createProducerProperties() { - return new ExtendedProducerProperties<>(new KafkaProducerProperties()); - } - - @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"); - } - - @Test - public void testDlqAndRetry() { - KafkaTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - DirectChannel moduleInputChannel = new DirectChannel(); - QueueChannel dlqChannel = new QueueChannel(); - FailingInvocationCountingMessageHandler handler = new FailingInvocationCountingMessageHandler(); - moduleInputChannel.subscribe(handler); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(10); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setMaxAttempts(3); - consumerProperties.setBackOffInitialInterval(100); - consumerProperties.setBackOffMaxInterval(150); - consumerProperties.getExtension().setEnableDlq(true); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("retryTest." + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("retryTest." + uniqueBindingId + ".0", - "testGroup", moduleInputChannel, consumerProperties); - - ExtendedConsumerProperties dlqConsumerProperties = createConsumerProperties(); - dlqConsumerProperties.setMaxAttempts(1); - - Binding dlqConsumerBinding = binder.bindConsumer( - "error.retryTest." + uniqueBindingId + ".0.testGroup", null, dlqChannel, dlqConsumerProperties); - - String testMessagePayload = "test." + UUID.randomUUID().toString(); - Message testMessage = MessageBuilder.withPayload(testMessagePayload).build(); - moduleOutputChannel.send(testMessage); - - Message receivedMessage = receive(dlqChannel, 3); - assertThat(receivedMessage).isNotNull(); - assertThat(receivedMessage.getPayload()).isEqualTo(testMessagePayload); - assertThat(handler.getInvocationCount()).isEqualTo(consumerProperties.getMaxAttempts()); - dlqConsumerBinding.unbind(); - consumerBinding.unbind(); - producerBinding.unbind(); - } - - @Test - public void testDefaultAutoCommitOnErrorWithoutDlq() throws Exception { - KafkaTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - DirectChannel moduleInputChannel = new DirectChannel(); - FailingInvocationCountingMessageHandler handler = new FailingInvocationCountingMessageHandler(); - moduleInputChannel.subscribe(handler); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(10); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setMaxAttempts(1); - consumerProperties.setBackOffInitialInterval(100); - consumerProperties.setBackOffMaxInterval(150); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("retryTest." + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("retryTest." + uniqueBindingId + ".0", - "testGroup", moduleInputChannel, consumerProperties); - - String testMessagePayload = "test." + UUID.randomUUID().toString(); - Message testMessage = MessageBuilder.withPayload(testMessagePayload).build(); - moduleOutputChannel.send(testMessage); - - assertThat(handler.getLatch().await((int) (timeoutMultiplier * 1000), TimeUnit.MILLISECONDS)); - // first attempt fails - assertThat(handler.getReceivedMessages().entrySet()).hasSize(1); - Message receivedMessage = handler.getReceivedMessages().entrySet().iterator().next().getValue(); - assertThat(receivedMessage).isNotNull(); - assertThat(receivedMessage.getPayload()).isEqualTo(testMessagePayload); - assertThat(handler.getInvocationCount()).isEqualTo(consumerProperties.getMaxAttempts()); - consumerBinding.unbind(); - - // on the second attempt the message is redelivered - QueueChannel successfulInputChannel = new QueueChannel(); - consumerBinding = binder.bindConsumer("retryTest." + uniqueBindingId + ".0", "testGroup", - successfulInputChannel, consumerProperties); - String testMessage2Payload = "test." + UUID.randomUUID().toString(); - Message testMessage2 = MessageBuilder.withPayload(testMessage2Payload).build(); - moduleOutputChannel.send(testMessage2); - - Message firstReceived = receive(successfulInputChannel); - assertThat(firstReceived.getPayload()).isEqualTo(testMessagePayload); - Message secondReceived = receive(successfulInputChannel); - assertThat(secondReceived.getPayload()).isEqualTo(testMessage2Payload); - consumerBinding.unbind(); - producerBinding.unbind(); - } - - @Test - public void testDefaultAutoCommitOnErrorWithDlq() throws Exception { - KafkaTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - DirectChannel moduleInputChannel = new DirectChannel(); - FailingInvocationCountingMessageHandler handler = new FailingInvocationCountingMessageHandler(); - moduleInputChannel.subscribe(handler); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(10); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setMaxAttempts(3); - consumerProperties.setBackOffInitialInterval(100); - consumerProperties.setBackOffMaxInterval(150); - consumerProperties.getExtension().setEnableDlq(true); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("retryTest." + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("retryTest." + uniqueBindingId + ".0", - "testGroup", moduleInputChannel, consumerProperties); - ExtendedConsumerProperties dlqConsumerProperties = createConsumerProperties(); - dlqConsumerProperties.setMaxAttempts(1); - QueueChannel dlqChannel = new QueueChannel(); - Binding dlqConsumerBinding = binder.bindConsumer( - "error.retryTest." + uniqueBindingId + ".0.testGroup", null, dlqChannel, dlqConsumerProperties); - - String testMessagePayload = "test." + UUID.randomUUID().toString(); - Message testMessage = MessageBuilder.withPayload(testMessagePayload).build(); - moduleOutputChannel.send(testMessage); - - Message dlqMessage = receive(dlqChannel, 3); - assertThat(dlqMessage).isNotNull(); - assertThat(dlqMessage.getPayload()).isEqualTo(testMessagePayload); - - // first attempt fails - assertThat(handler.getReceivedMessages().entrySet()).hasSize(1); - Message handledMessage = handler.getReceivedMessages().entrySet().iterator().next().getValue(); - assertThat(handledMessage).isNotNull(); - assertThat(handledMessage.getPayload()).isEqualTo(testMessagePayload); - assertThat(handler.getInvocationCount()).isEqualTo(consumerProperties.getMaxAttempts()); - - dlqConsumerBinding.unbind(); - consumerBinding.unbind(); - - // on the second attempt the message is not redelivered because the DLQ is set - QueueChannel successfulInputChannel = new QueueChannel(); - consumerBinding = binder.bindConsumer("retryTest." + uniqueBindingId + ".0", "testGroup", - successfulInputChannel, consumerProperties); - String testMessage2Payload = "test." + UUID.randomUUID().toString(); - Message testMessage2 = MessageBuilder.withPayload(testMessage2Payload).build(); - moduleOutputChannel.send(testMessage2); - - Message receivedMessage = receive(successfulInputChannel); - assertThat(receivedMessage.getPayload()).isEqualTo(testMessage2Payload); - - consumerBinding.unbind(); - producerBinding.unbind(); - } - - @Test(expected = IllegalArgumentException.class) - public void testValidateKafkaTopicName() { - KafkaMessageChannelBinder.validateTopicName("foo:bar"); - } - - @Test - public void testCompression() throws Exception { - final ProducerMetadata.CompressionType[] codecs = new ProducerMetadata.CompressionType[] { - ProducerMetadata.CompressionType.none, ProducerMetadata.CompressionType.gzip, - ProducerMetadata.CompressionType.snappy }; - byte[] testPayload = new byte[2048]; - Arrays.fill(testPayload, (byte) 65); - KafkaTestBinder binder = getBinder(); - for (ProducerMetadata.CompressionType codec : codecs) { - DirectChannel moduleOutputChannel = new DirectChannel(); - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.getExtension().setCompressionType(codec); - Binding producerBinding = binder.bindProducer("foo.0", moduleOutputChannel, - producerProperties); - Binding consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel, - createConsumerProperties()); - Message message = org.springframework.integration.support.MessageBuilder.withPayload(testPayload) - .build(); - // Let the consumer actually bind to the producer before sending a msg - binderBindUnbindLatency(); - moduleOutputChannel.send(message); - Message inbound = receive(moduleInputChannel); - assertThat(inbound).isNotNull(); - assertThat((byte[]) inbound.getPayload()).containsExactly(testPayload); - producerBinding.unbind(); - consumerBinding.unbind(); - } - } - - @Test - public void testCustomPartitionCountOverridesDefaultIfLarger() throws Exception { - - byte[] testPayload = new byte[2048]; - Arrays.fill(testPayload, (byte) 65); - KafkaBinderConfigurationProperties binderConfiguration = createConfigurationProperties(); - binderConfiguration.setMinPartitionCount(10); - KafkaTestBinder binder = new KafkaTestBinder(binderConfiguration); - - DirectChannel moduleOutputChannel = new DirectChannel(); - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(10); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, - moduleInputChannel, consumerProperties); - Message message = org.springframework.integration.support.MessageBuilder.withPayload(testPayload) - .build(); - // Let the consumer actually bind to the producer before sending a msg - binderBindUnbindLatency(); - moduleOutputChannel.send(message); - Message inbound = receive(moduleInputChannel); - assertThat(inbound).isNotNull(); - assertThat((byte[]) inbound.getPayload()).containsExactly(testPayload); - Collection partitions = binder.getCoreBinder().getConnectionFactory() - .getPartitions("foo" + uniqueBindingId + ".0"); - assertThat(partitions).hasSize(10); - producerBinding.unbind(); - consumerBinding.unbind(); - } - - @Test - public void testCustomPartitionCountDoesNotOverridePartitioningIfSmaller() throws Exception { - - byte[] testPayload = new byte[2048]; - Arrays.fill(testPayload, (byte) 65); - KafkaBinderConfigurationProperties binderConfiguration = createConfigurationProperties(); - binderConfiguration.setMinPartitionCount(6); - KafkaTestBinder binder = new KafkaTestBinder(binderConfiguration); - DirectChannel moduleOutputChannel = new DirectChannel(); - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(5); - producerProperties.setPartitionKeyExpression(spelExpressionParser.parseExpression("payload")); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, - moduleInputChannel, consumerProperties); - Message message = org.springframework.integration.support.MessageBuilder.withPayload(testPayload) - .build(); - // Let the consumer actually bind to the producer before sending a msg - binderBindUnbindLatency(); - moduleOutputChannel.send(message); - Message inbound = receive(moduleInputChannel); - assertThat(inbound).isNotNull(); - assertThat((byte[]) inbound.getPayload()).containsExactly(testPayload); - Collection partitions = binder.getCoreBinder().getConnectionFactory() - .getPartitions("foo" + uniqueBindingId + ".0"); - assertThat(partitions).hasSize(6); - producerBinding.unbind(); - consumerBinding.unbind(); - } - - @Test - public void testCustomPartitionCountOverridesPartitioningIfLarger() throws Exception { - - byte[] testPayload = new byte[2048]; - Arrays.fill(testPayload, (byte) 65); - KafkaBinderConfigurationProperties binderConfiguration = createConfigurationProperties(); - binderConfiguration.setMinPartitionCount(4); - KafkaTestBinder binder = new KafkaTestBinder(binderConfiguration); - - DirectChannel moduleOutputChannel = new DirectChannel(); - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setPartitionCount(5); - producerProperties.setPartitionKeyExpression(spelExpressionParser.parseExpression("payload")); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - long uniqueBindingId = System.currentTimeMillis(); - Binding producerBinding = binder.bindProducer("foo" + uniqueBindingId + ".0", - moduleOutputChannel, producerProperties); - Binding consumerBinding = binder.bindConsumer("foo" + uniqueBindingId + ".0", null, - moduleInputChannel, consumerProperties); - Message message = org.springframework.integration.support.MessageBuilder.withPayload(testPayload) - .build(); - // Let the consumer actually bind to the producer before sending a msg - binderBindUnbindLatency(); - moduleOutputChannel.send(message); - Message inbound = receive(moduleInputChannel); - assertThat(inbound).isNotNull(); - assertThat((byte[]) inbound.getPayload()).containsExactly(testPayload); - Collection partitions = binder.getCoreBinder().getConnectionFactory() - .getPartitions("foo" + uniqueBindingId + ".0"); - assertThat(partitions).hasSize(5); - producerBinding.unbind(); - consumerBinding.unbind(); - } - - @Test - @SuppressWarnings("unchecked") - public void testDefaultConsumerStartsAtEarliest() throws Exception { - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(createConfigurationProperties()); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - QueueChannel input1 = new QueueChannel(); - - String testTopicName = UUID.randomUUID().toString(); - binder.bindProducer(testTopicName, output, createProducerProperties()); - String testPayload1 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload1.getBytes())); - binder.bindConsumer(testTopicName, "startOffsets", input1, createConsumerProperties()); - Message receivedMessage1 = (Message) receive(input1); - assertThat(receivedMessage1).isNotNull(); - assertThat(new String(receivedMessage1.getPayload())).isEqualTo(testPayload1); - String testPayload2 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload2.getBytes())); - Message receivedMessage2 = (Message) receive(input1); - assertThat(receivedMessage2).isNotNull(); - assertThat(new String(receivedMessage2.getPayload())).isEqualTo(testPayload2); - } - - @Test - @SuppressWarnings("unchecked") - public void testEarliest() throws Exception { - KafkaTestBinder binder = getBinder(); - DirectChannel output = new DirectChannel(); - QueueChannel input1 = new QueueChannel(); - - String testTopicName = UUID.randomUUID().toString(); - binder.bindProducer(testTopicName, output, createProducerProperties()); - String testPayload1 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload1.getBytes())); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setStartOffset(KafkaMessageChannelBinder.StartOffset.earliest); - binder.bindConsumer(testTopicName, "startOffsets", input1, properties); - Message receivedMessage1 = (Message) receive(input1); - assertThat(receivedMessage1).isNotNull(); - String testPayload2 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload2.getBytes())); - Message receivedMessage2 = (Message) receive(input1); - assertThat(receivedMessage2).isNotNull(); - assertThat(new String(receivedMessage2.getPayload())).isEqualTo(testPayload2); - } - - @Test - @SuppressWarnings("unchecked") - public void testReset() throws Exception { - KafkaTestBinder binder = getBinder(); - DirectChannel output = new DirectChannel(); - QueueChannel input1 = new QueueChannel(); - - String testTopicName = UUID.randomUUID().toString(); - - Binding producerBinding = binder.bindProducer(testTopicName, output, - createProducerProperties()); - String testPayload1 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload1.getBytes())); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setResetOffsets(true); - properties.getExtension().setStartOffset(KafkaMessageChannelBinder.StartOffset.earliest); - Binding consumerBinding = binder.bindConsumer(testTopicName, "startOffsets", input1, - properties); - Message receivedMessage1 = (Message) receive(input1); - assertThat(receivedMessage1).isNotNull(); - String testPayload2 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload2.getBytes())); - Message receivedMessage2 = (Message) receive(input1); - assertThat(receivedMessage2).isNotNull(); - assertThat(new String(receivedMessage2.getPayload())).isEqualTo(testPayload2); - consumerBinding.unbind(); - - String testPayload3 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload3.getBytes())); - - ExtendedConsumerProperties properties2 = createConsumerProperties(); - properties2.getExtension().setResetOffsets(true); - properties2.getExtension().setStartOffset(KafkaMessageChannelBinder.StartOffset.earliest); - consumerBinding = binder.bindConsumer(testTopicName, "startOffsets", input1, properties2); - Message receivedMessage4 = (Message) receive(input1); - assertThat(receivedMessage4).isNotNull(); - assertThat(new String(receivedMessage4.getPayload())).isEqualTo(testPayload1); - Message receivedMessage5 = (Message) receive(input1); - assertThat(receivedMessage5).isNotNull(); - assertThat(new String(receivedMessage5.getPayload())).isEqualTo(testPayload2); - Message receivedMessage6 = (Message) receive(input1); - assertThat(receivedMessage6).isNotNull(); - assertThat(new String(receivedMessage6.getPayload())).isEqualTo(testPayload3); - consumerBinding.unbind(); - producerBinding.unbind(); - } - - @Test - @SuppressWarnings("unchecked") - public void testResume() throws Exception { - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - QueueChannel input1 = new QueueChannel(); - - String testTopicName = UUID.randomUUID().toString(); - Binding producerBinding = binder.bindProducer(testTopicName, output, - createProducerProperties()); - String testPayload1 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload1.getBytes())); - ExtendedConsumerProperties firstConsumerProperties = createConsumerProperties(); - Binding consumerBinding = binder.bindConsumer(testTopicName, "startOffsets", input1, - firstConsumerProperties); - Message receivedMessage1 = (Message) receive(input1); - assertThat(receivedMessage1).isNotNull(); - String testPayload2 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload2.getBytes())); - Message receivedMessage2 = (Message) receive(input1); - assertThat(receivedMessage2).isNotNull(); - assertThat(new String(receivedMessage2.getPayload())).isNotNull(); - consumerBinding.unbind(); - - String testPayload3 = "foo-" + UUID.randomUUID().toString(); - output.send(new GenericMessage<>(testPayload3.getBytes())); - - consumerBinding = binder.bindConsumer(testTopicName, "startOffsets", input1, createConsumerProperties()); - Message receivedMessage3 = (Message) receive(input1); - assertThat(receivedMessage3).isNotNull(); - assertThat(new String(receivedMessage3.getPayload())).isEqualTo(testPayload3); - consumerBinding.unbind(); - producerBinding.unbind(); - } - - @Test - public void testSyncProducerMetadata() throws Exception { - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(createConfigurationProperties()); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - String testTopicName = UUID.randomUUID().toString(); - ExtendedProducerProperties properties = createProducerProperties(); - properties.getExtension().setSync(true); - Binding producerBinding = binder.bindProducer(testTopicName, output, properties); - DirectFieldAccessor accessor = new DirectFieldAccessor(extractEndpoint(producerBinding)); - MessageHandler handler = (MessageHandler) accessor.getPropertyValue("handler"); - DirectFieldAccessor accessor1 = new DirectFieldAccessor(handler); - ProducerConfiguration producerConfiguration = (ProducerConfiguration) accessor1 - .getPropertyValue("producerConfiguration"); - assertThat(producerConfiguration.getProducerMetadata().isSync()) - .withFailMessage("Kafka Sync Producer should have been enabled."); - producerBinding.unbind(); - } - - @Test - public void testAutoCreateTopicsDisabledFailsIfTopicMissing() throws Exception { - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoCreateTopics(false); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - RetryTemplate metatadataRetrievalRetryOperations = new RetryTemplate(); - metatadataRetrievalRetryOperations.setRetryPolicy(new SimpleRetryPolicy()); - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - backOffPolicy.setBackOffPeriod(1000); - metatadataRetrievalRetryOperations.setBackOffPolicy(backOffPolicy); - binder.setMetadataRetryOperations(metatadataRetrievalRetryOperations); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - String testTopicName = "nonexisting" + System.currentTimeMillis(); - try { - binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - fail(); - } - catch (Exception e) { - assertThat(e).isInstanceOf(BinderException.class); - assertThat(e).hasMessageContaining("Topic " + testTopicName + " does not exist"); - } - - try { - binder.getConnectionFactory().getPartitions(testTopicName); - fail(); - } - catch (Exception e) { - assertThat(e).isInstanceOf(TopicNotFoundException.class); - } - } - - @Test - public void testAutoConfigureTopicsDisabledSucceedsIfTopicExisting() throws Exception { - String testTopicName = "existing" + System.currentTimeMillis(); - AdminUtils.createTopic(kafkaTestSupport.getZkClient(), testTopicName, 5, 1, new Properties()); - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoCreateTopics(false); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - Binding binding = binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - binding.unbind(); - } - - @Test - public void testAutoAddPartitionsDisabledFailsIfTopicUnderpartitioned() throws Exception { - String testTopicName = "existing" + System.currentTimeMillis(); - AdminUtils.createTopic(kafkaTestSupport.getZkClient(), testTopicName, 1, 1, new Properties()); - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoAddPartitions(false); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - // this consumer must consume from partition 2 - consumerProperties.setInstanceCount(3); - consumerProperties.setInstanceIndex(2); - try { - binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - } - catch (Exception e) { - assertThat(e).isInstanceOf(BinderException.class); - assertThat(e) - .hasMessageContaining("The number of expected partitions was: 3, but 1 has been found instead"); - } - } - - @Test - public void testAutoAddPartitionsDisabledSucceedsIfTopicPartitionedCorrectly() throws Exception { - - String testTopicName = "existing" + System.currentTimeMillis(); - AdminUtils.createTopic(kafkaTestSupport.getZkClient(), testTopicName, 6, 1, new Properties()); - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoAddPartitions(false); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - RetryTemplate metatadataRetrievalRetryOperations = new RetryTemplate(); - metatadataRetrievalRetryOperations.setRetryPolicy(new SimpleRetryPolicy()); - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - backOffPolicy.setBackOffPeriod(1000); - metatadataRetrievalRetryOperations.setBackOffPolicy(backOffPolicy); - binder.setMetadataRetryOperations(metatadataRetrievalRetryOperations); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - // this consumer must consume from partition 2 - consumerProperties.setInstanceCount(3); - consumerProperties.setInstanceIndex(2); - - Binding binding = binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - - Partition[] listenedPartitions = TestUtils.getPropertyValue(binding, - "endpoint.val$messageListenerContainer.partitions", Partition[].class); - - assertThat(listenedPartitions).hasSize(2); - assertThat(listenedPartitions).contains(new Partition(testTopicName, 2), new Partition(testTopicName, 5)); - Collection partitions = binder.getConnectionFactory().getPartitions(testTopicName); - assertThat(partitions).hasSize(6); - binding.unbind(); - } - - @Test - public void testAutoCreateTopicsEnabledSucceeds() throws Exception { - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoCreateTopics(true); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - RetryTemplate metatadataRetrievalRetryOperations = new RetryTemplate(); - metatadataRetrievalRetryOperations.setRetryPolicy(new SimpleRetryPolicy()); - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - backOffPolicy.setBackOffPeriod(1000); - metatadataRetrievalRetryOperations.setBackOffPolicy(backOffPolicy); - binder.setMetadataRetryOperations(metatadataRetrievalRetryOperations); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - String testTopicName = "nonexisting" + System.currentTimeMillis(); - Binding binding = binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - binding.unbind(); - } - - @Test - public void testPartitionCountNotReduced() throws Exception { - String testTopicName = "existing" + System.currentTimeMillis(); - AdminUtils.createTopic(kafkaTestSupport.getZkClient(), testTopicName, 6, 1, new Properties()); - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setAutoAddPartitions(true); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - RetryTemplate metatadataRetrievalRetryOperations = new RetryTemplate(); - metatadataRetrievalRetryOperations.setRetryPolicy(new SimpleRetryPolicy()); - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - backOffPolicy.setBackOffPeriod(1000); - metatadataRetrievalRetryOperations.setBackOffPolicy(backOffPolicy); - binder.setMetadataRetryOperations(metatadataRetrievalRetryOperations); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - Binding binding = binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - binding.unbind(); - TopicMetadata topicMetadata = AdminUtils.fetchTopicMetadataFromZk(testTopicName, - kafkaTestSupport.getZkClient()); - assertThat(topicMetadata.partitionsMetadata().size()).isEqualTo(6); - } - - @Test - public void testPartitionCountIncreasedIfAutoAddPartitionsSet() throws Exception { - String testTopicName = "existing" + System.currentTimeMillis(); - AdminUtils.createTopic(kafkaTestSupport.getZkClient(), testTopicName, 1, 1, new Properties()); - KafkaBinderConfigurationProperties configurationProperties = createConfigurationProperties(); - configurationProperties.setMinPartitionCount(6); - configurationProperties.setAutoAddPartitions(true); - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(configurationProperties); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - RetryTemplate metatadataRetrievalRetryOperations = new RetryTemplate(); - metatadataRetrievalRetryOperations.setRetryPolicy(new SimpleRetryPolicy()); - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - backOffPolicy.setBackOffPeriod(1000); - metatadataRetrievalRetryOperations.setBackOffPolicy(backOffPolicy); - binder.setMetadataRetryOperations(metatadataRetrievalRetryOperations); - DirectChannel output = new DirectChannel(); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - Binding binding = binder.doBindConsumer(testTopicName, "test", output, consumerProperties); - binding.unbind(); - TopicMetadata topicMetadata = AdminUtils.fetchTopicMetadataFromZk(testTopicName, - kafkaTestSupport.getZkClient()); - assertThat(topicMetadata.partitionsMetadata().size()).isEqualTo(6); - } - - private static final class FailingInvocationCountingMessageHandler implements MessageHandler { - - private int invocationCount; - - private final LinkedHashMap> receivedMessages = new LinkedHashMap<>(); - - private final CountDownLatch latch; - - private FailingInvocationCountingMessageHandler(int latchSize) { - latch = new CountDownLatch(latchSize); - } - - private FailingInvocationCountingMessageHandler() { - this(1); - } - - @Override - public void handleMessage(Message message) throws MessagingException { - invocationCount++; - Long offset = message.getHeaders().get(KafkaHeaders.OFFSET, Long.class); - // using the offset as key allows to ensure that we don't store duplicate - // messages on retry - if (!receivedMessages.containsKey(offset)) { - receivedMessages.put(offset, message); - latch.countDown(); - } - throw new RuntimeException(); - } - - public LinkedHashMap> getReceivedMessages() { - return receivedMessages; - } - - public int getInvocationCount() { - return invocationCount; - } - - public CountDownLatch getLatch() { - return latch; - } - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java deleted file mode 100644 index b58fd7fc2..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/KafkaTestBinder.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2015-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.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.test.junit.kafka.TestKafkaCluster; -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.kafka.support.LoggingProducerListener; -import org.springframework.integration.kafka.support.ProducerListener; -import org.springframework.integration.tuple.TupleKryoRegistrar; - -/** - * Test support class for {@link KafkaMessageChannelBinder}. Creates a binder that uses a - * test {@link TestKafkaCluster kafka cluster}. - * @author Eric Bottard - * @author Marius Bogoevici - * @author David Turanski - * @author Gary Russell - * @author Soby Chacko - */ -public class KafkaTestBinder extends - AbstractTestBinder, ExtendedProducerProperties> { - - public KafkaTestBinder(KafkaBinderConfigurationProperties binderConfiguration) { - try { - KafkaMessageChannelBinder binder = new KafkaMessageChannelBinder(binderConfiguration); - binder.setCodec(getCodec()); - ProducerListener producerListener = new LoggingProducerListener(); - binder.setProducerListener(producerListener); - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - binder.setApplicationContext(context); - binder.afterPropertiesSet(); - this.setBinder(binder); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - @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-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawKafkaPartitionTestSupport.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawKafkaPartitionTestSupport.java deleted file mode 100644 index b0f8da029..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawKafkaPartitionTestSupport.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 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 org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy; -import org.springframework.cloud.stream.binder.PartitionSelectorStrategy; -import org.springframework.messaging.Message; - -/** - * @author Marius Bogoevici - */ -public class RawKafkaPartitionTestSupport implements PartitionKeyExtractorStrategy, PartitionSelectorStrategy { - - @Override - public int selectPartition(Object key, int divisor) { - return ((byte[]) key)[0] % divisor; - } - - @Override - public Object extractKey(Message message) { - return message.getPayload(); - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawModeKafkaBinderTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawModeKafkaBinderTests.java deleted file mode 100644 index 775d2428e..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka/src/test/java/org/springframework/cloud/stream/binder/kafka/RawModeKafkaBinderTests.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2015-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.Arrays; - -import org.junit.Ignore; -import org.junit.Test; - -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.ExtendedProducerProperties; -import org.springframework.cloud.stream.binder.HeaderMode; -import org.springframework.integration.IntegrationMessageHeaderAccessor; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.support.GenericMessage; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Marius Bogoevici - * @author David Turanski - * @author Gary Russell - * @author Mark Fisher - */ -public class RawModeKafkaBinderTests extends KafkaBinderTests { - - @Test - @Override - public void testPartitionedModuleJava() throws Exception { - KafkaTestBinder binder = getBinder(); - ExtendedProducerProperties properties = createProducerProperties(); - properties.setHeaderMode(HeaderMode.raw); - properties.setPartitionKeyExtractorClass(RawKafkaPartitionTestSupport.class); - properties.setPartitionSelectorClass(RawKafkaPartitionTestSupport.class); - properties.setPartitionCount(6); - - DirectChannel output = new DirectChannel(); - output.setBeanName("test.output"); - Binding outputBinding = binder.bindProducer("partJ.0", output, properties); - - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setConcurrency(2); - consumerProperties.setInstanceCount(3); - consumerProperties.setInstanceIndex(0); - consumerProperties.setPartitioned(true); - consumerProperties.setHeaderMode(HeaderMode.raw); - QueueChannel input0 = new QueueChannel(); - input0.setBeanName("test.input0J"); - Binding input0Binding = binder.bindConsumer("partJ.0", "test", input0, consumerProperties); - consumerProperties.setInstanceIndex(1); - QueueChannel input1 = new QueueChannel(); - input1.setBeanName("test.input1J"); - Binding input1Binding = binder.bindConsumer("partJ.0", "test", input1, consumerProperties); - consumerProperties.setInstanceIndex(2); - QueueChannel input2 = new QueueChannel(); - input2.setBeanName("test.input2J"); - Binding input2Binding = binder.bindConsumer("partJ.0", "test", input2, consumerProperties); - - output.send(new GenericMessage<>(new byte[] { (byte) 0 })); - output.send(new GenericMessage<>(new byte[] { (byte) 1 })); - output.send(new GenericMessage<>(new byte[] { (byte) 2 })); - - Message receive0 = receive(input0); - assertThat(receive0).isNotNull(); - Message receive1 = receive(input1); - assertThat(receive1).isNotNull(); - Message receive2 = receive(input2); - assertThat(receive2).isNotNull(); - - assertThat(Arrays.asList(((byte[]) receive0.getPayload())[0], ((byte[]) receive1.getPayload())[0], - ((byte[]) receive2.getPayload())[0])).containsExactlyInAnyOrder((byte) 0, (byte) 1, (byte) 2); - - input0Binding.unbind(); - input1Binding.unbind(); - input2Binding.unbind(); - outputBinding.unbind(); - } - - @Test - @Override - public void testPartitionedModuleSpEL() throws Exception { - KafkaTestBinder binder = getBinder(); - ExtendedProducerProperties properties = createProducerProperties(); - properties.setPartitionKeyExpression(spelExpressionParser.parseExpression("payload[0]")); - properties.setPartitionSelectorExpression(spelExpressionParser.parseExpression("hashCode()")); - properties.setPartitionCount(6); - properties.setHeaderMode(HeaderMode.raw); - - DirectChannel output = new DirectChannel(); - output.setBeanName("test.output"); - Binding outputBinding = binder.bindProducer("part.0", output, properties); - try { - AbstractEndpoint endpoint = extractEndpoint(outputBinding); - assertThat(getEndpointRouting(endpoint)).contains("part.0-' + headers['partition']"); - } - catch (UnsupportedOperationException ignored) { - - } - - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setConcurrency(2); - consumerProperties.setInstanceIndex(0); - consumerProperties.setInstanceCount(3); - consumerProperties.setPartitioned(true); - consumerProperties.setHeaderMode(HeaderMode.raw); - QueueChannel input0 = new QueueChannel(); - input0.setBeanName("test.input0S"); - Binding input0Binding = binder.bindConsumer("part.0", "test", input0, consumerProperties); - consumerProperties.setInstanceIndex(1); - QueueChannel input1 = new QueueChannel(); - input1.setBeanName("test.input1S"); - Binding input1Binding = binder.bindConsumer("part.0", "test", input1, consumerProperties); - consumerProperties.setInstanceIndex(2); - QueueChannel input2 = new QueueChannel(); - input2.setBeanName("test.input2S"); - Binding input2Binding = binder.bindConsumer("part.0", "test", input2, consumerProperties); - - Message message2 = MessageBuilder.withPayload(new byte[] { 2 }) - .setHeader(IntegrationMessageHeaderAccessor.CORRELATION_ID, "foo") - .setHeader(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, 42) - .setHeader(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, 43).build(); - output.send(message2); - output.send(new GenericMessage<>(new byte[] { 1 })); - output.send(new GenericMessage<>(new byte[] { 0 })); - Message receive0 = receive(input0); - assertThat(receive0).isNotNull(); - Message receive1 = receive(input1); - assertThat(receive1).isNotNull(); - Message receive2 = receive(input2); - assertThat(receive2).isNotNull(); - assertThat(Arrays.asList(((byte[]) receive0.getPayload())[0], ((byte[]) receive1.getPayload())[0], - ((byte[]) receive2.getPayload())[0])).containsExactlyInAnyOrder((byte) 0, (byte) 1, (byte) 2); - input0Binding.unbind(); - input1Binding.unbind(); - input2Binding.unbind(); - outputBinding.unbind(); - } - - @Test - @Override - public void testSendAndReceive() throws Exception { - KafkaTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setHeaderMode(HeaderMode.raw); - Binding producerBinding = binder.bindProducer("foo.0", moduleOutputChannel, producerProperties); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setHeaderMode(HeaderMode.raw); - Binding consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel, - consumerProperties); - Message message = MessageBuilder.withPayload("foo".getBytes()).build(); - // Let the consumer actually bind to the producer before sending a msg - binderBindUnbindLatency(); - moduleOutputChannel.send(message); - Message inbound = receive(moduleInputChannel); - assertThat(inbound).isNotNull(); - assertThat(new String((byte[]) inbound.getPayload())).isEqualTo("foo"); - producerBinding.unbind(); - consumerBinding.unbind(); - } - - // Ignored, since raw mode does not support headers - @Test - @Override - @Ignore - public void testSendAndReceiveNoOriginalContentType() throws Exception { - - } - - @Test - public void testSendAndReceiveWithExplicitConsumerGroup() { - KafkaTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - // Test pub/sub by emulating how StreamPlugin handles taps - QueueChannel module1InputChannel = new QueueChannel(); - QueueChannel module2InputChannel = new QueueChannel(); - QueueChannel module3InputChannel = new QueueChannel(); - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.setHeaderMode(HeaderMode.raw); - Binding producerBinding = binder.bindProducer("baz.0", moduleOutputChannel, producerProperties); - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.setHeaderMode(HeaderMode.raw); - Binding input1Binding = binder.bindConsumer("baz.0", "test", module1InputChannel, - consumerProperties); - // A new module is using the tap as an input channel - String fooTapName = "baz.0"; - Binding input2Binding = binder.bindConsumer(fooTapName, "tap1", module2InputChannel, - consumerProperties); - // Another new module is using tap as an input channel - String barTapName = "baz.0"; - Binding input3Binding = binder.bindConsumer(barTapName, "tap2", module3InputChannel, - consumerProperties); - - Message message = MessageBuilder.withPayload("foo".getBytes()).build(); - boolean success = false; - boolean retried = false; - while (!success) { - moduleOutputChannel.send(message); - Message inbound = receive(module1InputChannel); - assertThat(inbound).isNotNull(); - assertThat(new String((byte[]) inbound.getPayload())).isEqualTo("foo"); - - Message tapped1 = receive(module2InputChannel); - Message tapped2 = receive(module3InputChannel); - if (tapped1 == null || tapped2 == null) { - // listener may not have started - assertThat(retried).isFalse().withFailMessage("Failed to receive tap after retry"); - retried = true; - continue; - } - success = true; - assertThat(new String((byte[]) tapped1.getPayload())).isEqualTo("foo"); - assertThat(new String((byte[]) tapped2.getPayload())).isEqualTo("foo"); - } - // delete one tap stream is deleted - input3Binding.unbind(); - Message message2 = MessageBuilder.withPayload("bar".getBytes()).build(); - moduleOutputChannel.send(message2); - - // other tap still receives messages - Message tapped = receive(module2InputChannel); - assertThat(tapped).isNotNull(); - - // removed tap does not - assertThat(receive(module3InputChannel)).isNull(); - - // re-subscribed tap does receive the message - input3Binding = binder.bindConsumer(barTapName, "tap2", module3InputChannel, createConsumerProperties()); - assertThat(receive(module3InputChannel)).isNotNull(); - - // clean up - input1Binding.unbind(); - input2Binding.unbind(); - input3Binding.unbind(); - producerBinding.unbind(); - assertThat(extractEndpoint(input1Binding).isRunning()).isFalse(); - assertThat(extractEndpoint(input2Binding).isRunning()).isFalse(); - assertThat(extractEndpoint(input3Binding).isRunning()).isFalse(); - assertThat(extractEndpoint(producerBinding).isRunning()).isFalse(); - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/pom.xml b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/pom.xml deleted file mode 100644 index 39d52208a..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - 4.0.0 - - spring-cloud-stream-binder-rabbit - jar - spring-cloud-stream-binder-rabbit - RabbitMQ binder implementation - - - org.springframework.cloud - spring-cloud-stream-binders-parent - 1.1.0.BUILD-SNAPSHOT - - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.cloud - spring-cloud-stream - - - org.springframework.cloud - spring-cloud-stream-codec - - - org.springframework.boot - spring-boot-autoconfigure - true - - - org.springframework.cloud - spring-cloud-core - true - - - org.springframework.cloud - spring-cloud-stream-binder-test - test - - - org.springframework.cloud - spring-cloud-stream-test-support-internal - test - - - org.springframework.boot - spring-boot-starter-amqp - - - org.springframework.integration - spring-integration-amqp - - - diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitAdminException.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitAdminException.java deleted file mode 100644 index 191b2e2e1..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitAdminException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 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.rabbit; - - -/** - * Exceptions thrown while interfacing with the RabbitMQ admin plugin. - * @author Gary Russell - * @since 1.2 - */ -@SuppressWarnings("serial") -public class RabbitAdminException extends RuntimeException { - - public RabbitAdminException(String message, Throwable cause) { - super(message, cause); - } - - public RabbitAdminException(String message) { - super(message); - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingCleaner.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingCleaner.java deleted file mode 100644 index 250439595..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingCleaner.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2015-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.rabbit; - -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.stream.binder.AbstractBinder; -import org.springframework.cloud.stream.binder.BindingCleaner; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - - -/** - * Implementation of {@link org.springframework.cloud.stream.binder.BindingCleaner} for the {@code RabbitBinder}. - * @author Gary Russell - * @author David Turanski - * @since 1.2 - */ -public class RabbitBindingCleaner implements BindingCleaner { - - private static final Log logger = LogFactory.getLog(RabbitBindingCleaner.class); - - private static final String PREFIX_DELIMITER = "."; - - public static final String BINDER_PREFIX = "binder" + PREFIX_DELIMITER; - - @Override - public Map> clean(String entity, boolean isJob) { - return clean("http://localhost:15672", "guest", "guest", "/", BINDER_PREFIX, entity, isJob); - } - - public Map> clean(String adminUri, String user, String pw, String vhost, - String binderPrefix, String entity, boolean isJob) { - return doClean( - adminUri == null ? "http://localhost:15672" : adminUri, - user == null ? "guest" : user, - pw == null ? "guest" : pw, - vhost == null ? "/" : vhost, - binderPrefix == null ? BINDER_PREFIX : binderPrefix, - entity, isJob); - } - - private Map> doClean(String adminUri, String user, String pw, String vhost, - String binderPrefix, String entity, boolean isJob) { - RestTemplate restTemplate = RabbitManagementUtils.buildRestTemplate(adminUri, user, pw); - List removedQueues = isJob - ? null - : findStreamQueues(adminUri, vhost, binderPrefix, entity, restTemplate); - List removedExchanges = findExchanges(adminUri, vhost, binderPrefix, entity, restTemplate); - // Delete the queues in reverse order to enable re-running after a partial success. - // The queue search above starts with 0 and terminates on a not found. - for (int i = removedQueues.size() - 1; i >= 0; i--) { - String queueName = removedQueues.get(i); - URI uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("queues", "{vhost}", "{stream}") - .buildAndExpand(vhost, queueName).encode().toUri(); - restTemplate.delete(uri); - if (logger.isDebugEnabled()) { - logger.debug("deleted queue: " + queueName); - } - } - Map> results = new HashMap<>(); - if (removedQueues.size() > 0) { - results.put("queues", removedQueues); - } - // Fanout exchanges for taps - for (String exchange : removedExchanges) { - URI uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("exchanges", "{vhost}", "{name}") - .buildAndExpand(vhost, exchange).encode().toUri(); - restTemplate.delete(uri); - if (logger.isDebugEnabled()) { - logger.debug("deleted exchange: " + exchange); - } - } - if (removedExchanges.size() > 0) { - results.put("exchanges", removedExchanges); - } - return results; - } - - private List findStreamQueues(String adminUri, String vhost, String binderPrefix, String stream, - RestTemplate restTemplate) { - String queueNamePrefix = adjustPrefix(AbstractBinder.applyPrefix(binderPrefix, stream)); - List> queues = listAllQueues(adminUri, vhost, restTemplate); - List removedQueues = new ArrayList<>(); - for (Map queue : queues) { - String queueName = (String) queue.get("name"); - if (queueName.startsWith(queueNamePrefix)) { - checkNoConsumers(queueName, queue); - removedQueues.add(queueName); - } - } - return removedQueues; - } - - private List> listAllQueues(String adminUri, String vhost, RestTemplate restTemplate) { - URI uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("queues", "{vhost}") - .buildAndExpand(vhost).encode().toUri(); - @SuppressWarnings("unchecked") - List> queues = restTemplate.getForObject(uri, List.class); - return queues; - } - - private String adjustPrefix(String prefix) { - if (prefix.endsWith("*")) { - return prefix.substring(0, prefix.length() - 1); - } - else { - return prefix + PREFIX_DELIMITER; - } - } - - private void checkNoConsumers(String queueName, Map queue) { - if (!queue.get("consumers").equals(Integer.valueOf(0))) { - throw new RabbitAdminException("Queue " + queueName + " is in use"); - } - } - - @SuppressWarnings("unchecked") - private List findExchanges(String adminUri, String vhost, String binderPrefix, String entity, - RestTemplate restTemplate) { - List removedExchanges = new ArrayList<>(); - URI uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("exchanges", "{vhost}") - .buildAndExpand(vhost).encode().toUri(); - List> exchanges = restTemplate.getForObject(uri, List.class); - String exchangeNamePrefix = adjustPrefix(AbstractBinder.applyPrefix(binderPrefix, entity)); - for (Map exchange : exchanges) { - String exchangeName = (String) exchange.get("name"); - if (exchangeName.startsWith(exchangeNamePrefix)) { - uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("exchanges", "{vhost}", "{name}", "bindings", "source") - .buildAndExpand(vhost, exchangeName).encode().toUri(); - List> bindings = restTemplate.getForObject(uri, List.class); - if (hasNoForeignBindings(bindings, exchangeNamePrefix)) { - uri = UriComponentsBuilder.fromUriString(adminUri + "/api") - .pathSegment("exchanges", "{vhost}", "{name}", "bindings", "destination") - .buildAndExpand(vhost, exchangeName).encode().toUri(); - bindings = restTemplate.getForObject(uri, List.class); - if (bindings.size() == 0) { - removedExchanges.add((String) exchange.get("name")); - } - else { - throw new RabbitAdminException("Cannot delete exchange " + exchangeName - + "; it is a destination: " + bindings); - } - } - else { - throw new RabbitAdminException("Cannot delete exchange " + exchangeName + "; it has bindings: " - + bindings); - } - } - } - return removedExchanges; - } - - private boolean hasNoForeignBindings(List> bindings, String exchangeNamePrefix) { - if (bindings.size() == 0) { - return true; - } - boolean noForeign = true; - for (Map binding : bindings) { - if (!("queue".equals(binding.get("destination_type"))) - || !((String) binding.get("destination")).startsWith(exchangeNamePrefix)) { - noForeign = false; - break; - } - } - return noForeign; - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingProperties.java deleted file mode 100644 index 5ed04136d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitBindingProperties.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 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.rabbit; - -/** - * @author Marius Bogoevici - */ -public class RabbitBindingProperties { - - private RabbitConsumerProperties consumer = new RabbitConsumerProperties(); - - private RabbitProducerProperties producer = new RabbitProducerProperties(); - - public RabbitConsumerProperties getConsumer() { - return consumer; - } - - public void setConsumer(RabbitConsumerProperties consumer) { - this.consumer = consumer; - } - - public RabbitProducerProperties getProducer() { - return producer; - } - - public void setProducer(RabbitProducerProperties producer) { - this.producer = producer; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitConsumerProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitConsumerProperties.java deleted file mode 100644 index 7e24b4a58..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitConsumerProperties.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 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.rabbit; - -import javax.validation.constraints.Min; - -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.util.Assert; - -/** - * @author Marius Bogoevici - */ -public class RabbitConsumerProperties { - - private String prefix = ""; - - private boolean transacted; - - private AcknowledgeMode acknowledgeMode = AcknowledgeMode.AUTO; - - private int maxConcurrency = 1; - - private int prefetch = 1; - - private String[] requestHeaderPatterns = new String[] {"STANDARD_REQUEST_HEADERS", "*"}; - - private int txSize = 1; - - private boolean autoBindDlq; - - private boolean durableSubscription = true; - - private boolean republishToDlq; - - private boolean requeueRejected = true; - - private String[] replyHeaderPatterns = new String[] {"STANDARD_REPLY_HEADERS", "*"}; - - private long recoveryInterval = 5000; - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public boolean isTransacted() { - return transacted; - } - - public void setTransacted(boolean transacted) { - this.transacted = transacted; - } - - public AcknowledgeMode getAcknowledgeMode() { - return acknowledgeMode; - } - - public void setAcknowledgeMode(AcknowledgeMode acknowledgeMode) { - Assert.notNull("Acknowledge mode cannot be null"); - this.acknowledgeMode = acknowledgeMode; - } - - @Min(value = 1, message = "Max Concurrency should be greater than zero.") - public int getMaxConcurrency() { - return maxConcurrency; - } - - public void setMaxConcurrency(int maxConcurrency) { - this.maxConcurrency = maxConcurrency; - } - - @Min(value = 1, message = "Prefetch should be greater than zero.") - public int getPrefetch() { - return prefetch; - } - - public void setPrefetch(int prefetch) { - this.prefetch = prefetch; - } - - public String[] getRequestHeaderPatterns() { - return requestHeaderPatterns; - } - - public void setRequestHeaderPatterns(String[] requestHeaderPatterns) { - this.requestHeaderPatterns = requestHeaderPatterns; - } - - @Min(value = 1, message = "Tx Size should be greater than zero.") - public int getTxSize() { - return txSize; - } - - public void setTxSize(int txSize) { - this.txSize = txSize; - } - - public boolean isAutoBindDlq() { - return autoBindDlq; - } - - public void setAutoBindDlq(boolean autoBindDlq) { - this.autoBindDlq = autoBindDlq; - } - - public boolean isDurableSubscription() { - return durableSubscription; - } - - public void setDurableSubscription(boolean durableSubscription) { - this.durableSubscription = durableSubscription; - } - - public boolean isRepublishToDlq() { - return republishToDlq; - } - - public void setRepublishToDlq(boolean republishToDlq) { - this.republishToDlq = republishToDlq; - } - - public boolean isRequeueRejected() { - return requeueRejected; - } - - public void setRequeueRejected(boolean requeueRejected) { - this.requeueRejected = requeueRejected; - } - - public String[] getReplyHeaderPatterns() { - return replyHeaderPatterns; - } - - public void setReplyHeaderPatterns(String[] replyHeaderPatterns) { - this.replyHeaderPatterns = replyHeaderPatterns; - } - - public long getRecoveryInterval() { - return recoveryInterval; - } - - public void setRecoveryInterval(long recoveryInterval) { - this.recoveryInterval = recoveryInterval; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitExtendedBindingProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitExtendedBindingProperties.java deleted file mode 100644 index afc821c4f..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitExtendedBindingProperties.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 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.rabbit; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.stream.binder.ExtendedBindingProperties; - -/** - * @author Marius Bogoevici - */ -@ConfigurationProperties("spring.cloud.stream.rabbit") -public class RabbitExtendedBindingProperties implements ExtendedBindingProperties { - - private Map bindings = new HashMap<>(); - - public Map getBindings() { - return bindings; - } - - public void setBindings(Map bindings) { - this.bindings = bindings; - } - - @Override - public RabbitConsumerProperties getExtendedConsumerProperties(String channelName) { - if (bindings.containsKey(channelName) && bindings.get(channelName).getConsumer() != null) { - return bindings.get(channelName).getConsumer(); - } - else { - return new RabbitConsumerProperties(); - } - } - - @Override - public RabbitProducerProperties getExtendedProducerProperties(String channelName) { - if (bindings.containsKey(channelName) && bindings.get(channelName).getProducer() != null) { - return bindings.get(channelName).getProducer(); - } - else { - return new RabbitProducerProperties(); - } - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitManagementUtils.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitManagementUtils.java deleted file mode 100644 index 6ed3dcecf..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitManagementUtils.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2015 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.rabbit; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; - -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; -import org.apache.http.client.HttpClient; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicAuthCache; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.protocol.HttpContext; - -import org.springframework.http.HttpMethod; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.web.client.RestTemplate; - -/** - * @author Gary Russell - * @since 1.2 - */ -public abstract class RabbitManagementUtils { - - public static RestTemplate buildRestTemplate(String adminUri, String user, String password) { - BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), - new UsernamePasswordCredentials(user, password)); - HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build(); - // Set up pre-emptive basic Auth because the rabbit plugin doesn't currently support challenge/response for PUT - // Create AuthCache instance - AuthCache authCache = new BasicAuthCache(); - // Generate BASIC scheme object and add it to the local; from the apache docs... - // auth cache - BasicScheme basicAuth = new BasicScheme(); - URI uri; - try { - uri = new URI(adminUri); - } - catch (URISyntaxException e) { - throw new RabbitAdminException("Invalid URI", e); - } - authCache.put(new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()), basicAuth); - // Add AuthCache to the execution context - final HttpClientContext localContext = HttpClientContext.create(); - localContext.setAuthCache(authCache); - RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient) { - - @Override - protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { - return localContext; - } - - }); - restTemplate.setMessageConverters(Collections.>singletonList( - new MappingJackson2HttpMessageConverter())); - return restTemplate; - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitMessageChannelBinder.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitMessageChannelBinder.java deleted file mode 100644 index a996de14d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitMessageChannelBinder.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright 2013-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.rabbit; - -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; - -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Envelope; -import org.aopalliance.aop.Advice; - -import org.springframework.amqp.AmqpConnectException; -import org.springframework.amqp.core.AnonymousQueue; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.DirectExchange; -import org.springframework.amqp.core.Exchange; -import org.springframework.amqp.core.MessagePostProcessor; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.LocalizedQueueConnectionFactory; -import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.core.support.BatchingStrategy; -import org.springframework.amqp.rabbit.core.support.SimpleBatchingStrategy; -import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.rabbit.retry.MessageRecoverer; -import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer; -import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer; -import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; -import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; -import org.springframework.amqp.support.AmqpHeaders; -import org.springframework.amqp.support.postprocessor.DelegatingDecompressingPostProcessor; -import org.springframework.amqp.support.postprocessor.GZipPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties; -import org.springframework.cloud.stream.binder.AbstractBinder; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.DefaultBinding; -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.MessageValues; -import org.springframework.cloud.stream.binder.PartitionHandler; -import org.springframework.context.Lifecycle; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter; -import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; -import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.context.IntegrationContextUtils; -import org.springframework.integration.endpoint.EventDrivenConsumer; -import org.springframework.integration.handler.AbstractMessageHandler; -import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.SubscribableChannel; -import org.springframework.retry.interceptor.RetryOperationsInterceptor; -import org.springframework.scheduling.TaskScheduler; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * A {@link org.springframework.cloud.stream.binder.Binder} implementation backed by RabbitMQ. - * - * @author Mark Fisher - * @author Gary Russell - * @author Jennifer Hickey - * @author Gunnar Hillert - * @author Ilayaperumal Gopinathan - * @author David Turanski - * @author Marius Bogoevici - */ -public class RabbitMessageChannelBinder extends AbstractBinder, - ExtendedProducerProperties> implements ExtendedPropertiesBinder { - - public static final AnonymousQueue.Base64UrlNamingStrategy ANONYMOUS_GROUP_NAME_GENERATOR - = new AnonymousQueue.Base64UrlNamingStrategy("anonymous."); - - private static final String DEAD_LETTER_EXCHANGE = "DLX"; - - private static final MessagePropertiesConverter inboundMessagePropertiesConverter = - new DefaultMessagePropertiesConverter() { - - @Override - public MessageProperties toMessageProperties(AMQP.BasicProperties source, Envelope envelope, - String charset) { - MessageProperties properties = super.toMessageProperties(source, envelope, charset); - properties.setDeliveryMode(null); - return properties; - } - }; - - private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); - - private final RabbitAdmin rabbitAdmin; - - private final GenericApplicationContext autoDeclareContext = new GenericApplicationContext(); - - private final RabbitProperties rabbitProperties; - - private ConnectionFactory connectionFactory; - - private MessagePostProcessor decompressingPostProcessor = new DelegatingDecompressingPostProcessor(); - - private MessagePostProcessor compressingPostProcessor = new GZipPostProcessor(); - - private volatile String[] adminAddresses; - - private volatile String[] nodes; - - private volatile boolean clustered; - - private RabbitExtendedBindingProperties extendedBindingProperties = new RabbitExtendedBindingProperties(); - - public RabbitMessageChannelBinder(ConnectionFactory connectionFactory, RabbitProperties rabbitProperties) { - Assert.notNull(connectionFactory, "connectionFactory must not be null"); - Assert.notNull(rabbitProperties, "rabbitProperties must not be null"); - this.connectionFactory = connectionFactory; - this.rabbitProperties = rabbitProperties; - this.rabbitAdmin = new RabbitAdmin(connectionFactory); - this.autoDeclareContext.refresh(); - this.rabbitAdmin.setApplicationContext(this.autoDeclareContext); - this.rabbitAdmin.setIgnoreDeclarationExceptions(true); - this.rabbitAdmin.afterPropertiesSet(); - } - - /** - * Set a {@link MessagePostProcessor} to decompress messages. Defaults to a - * {@link DelegatingDecompressingPostProcessor} with its default delegates. - * @param decompressingPostProcessor the post processor. - */ - public void setDecompressingPostProcessor(MessagePostProcessor decompressingPostProcessor) { - this.decompressingPostProcessor = decompressingPostProcessor; - } - - /** - * Set a {@link org.springframework.amqp.core.MessagePostProcessor} to compress messages. Defaults to a - * {@link org.springframework.amqp.support.postprocessor.GZipPostProcessor}. - * @param compressingPostProcessor the post processor. - */ - public void setCompressingPostProcessor(MessagePostProcessor compressingPostProcessor) { - this.compressingPostProcessor = compressingPostProcessor; - } - - public void setAdminAddresses(String[] adminAddresses) { - this.adminAddresses = Arrays.copyOf(adminAddresses, adminAddresses.length); - } - - public void setNodes(String[] nodes) { - this.nodes = Arrays.copyOf(nodes, nodes.length); - this.clustered = nodes.length > 1; - } - - public void setExtendedBindingProperties(RabbitExtendedBindingProperties extendedBindingProperties) { - this.extendedBindingProperties = extendedBindingProperties; - } - - @Override - public void onInit() { - if (this.clustered) { - String[] addresses = StringUtils.commaDelimitedListToStringArray(this.rabbitProperties.getAddresses()); - Assert.state(addresses.length == this.adminAddresses.length - && addresses.length == this.nodes.length, - "'addresses', 'adminAddresses', and 'nodes' properties must have equal length"); - this.connectionFactory = new LocalizedQueueConnectionFactory(this.connectionFactory, addresses, - this.adminAddresses, this.nodes, rabbitProperties.getVirtualHost(), - this.rabbitProperties.getUsername(), this.rabbitProperties.getPassword(), - this.rabbitProperties.getSsl().isEnabled(), this.rabbitProperties.getSsl().getKeyStore(), - this.rabbitProperties.getSsl().getTrustStore(), this.rabbitProperties.getSsl().getKeyStorePassword(), - this.rabbitProperties.getSsl().getTrustStorePassword()); - } - } - - @Override - public RabbitConsumerProperties getExtendedConsumerProperties(String channelName) { - return extendedBindingProperties.getExtendedConsumerProperties(channelName); - } - - - @Override - public RabbitProducerProperties getExtendedProducerProperties(String channelName) { - return extendedBindingProperties.getExtendedProducerProperties(channelName); - } - - @Override - public Binding doBindConsumer(String name, String group, MessageChannel inputChannel, - ExtendedConsumerProperties properties) { - boolean anonymousConsumer = !StringUtils.hasText(group); - String baseQueueName = anonymousConsumer ? groupedName(name, ANONYMOUS_GROUP_NAME_GENERATOR.generateName()) - : groupedName(name, group); - if (this.logger.isInfoEnabled()) { - this.logger.info("declaring queue for inbound: " + baseQueueName + ", bound to: " + name); - } - String prefix = properties.getExtension().getPrefix(); - String exchangeName = applyPrefix(prefix, name); - TopicExchange exchange = new TopicExchange(exchangeName); - declareExchange(exchangeName, exchange); - - String queueName = applyPrefix(prefix, baseQueueName); - boolean partitioned = !anonymousConsumer && properties.isPartitioned(); - boolean durable = !anonymousConsumer && properties.getExtension().isDurableSubscription(); - Queue queue; - - if (anonymousConsumer) { - queue = new Queue(queueName, false, true, true); - } - else { - if (partitioned) { - String partitionSuffix = "-" + properties.getInstanceIndex(); - queueName += partitionSuffix; - } - if (durable) { - queue = new Queue(queueName, true, false, false, - queueArgs(queueName, properties.getExtension().getPrefix(), properties.getExtension().isAutoBindDlq())); - } - else { - queue = new Queue(queueName, false, false, true); - } - } - - declareQueue(queueName, queue); - - if (partitioned) { - String bindingKey = String.format("%s-%d", name, properties.getInstanceIndex()); - declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(bindingKey)); - } - else { - declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with("#")); - } - Binding binding = doRegisterConsumer(baseQueueName, group, inputChannel, queue, properties); - if (durable) { - autoBindDLQ(applyPrefix(prefix, baseQueueName), queueName, properties.getExtension().getPrefix(), properties.getExtension().isAutoBindDlq()); - } - return binding; - } - - private Map queueArgs(String queueName, String prefix, boolean bindDlq) { - Map args = new HashMap<>(); - if (bindDlq) { - args.put("x-dead-letter-exchange", applyPrefix(prefix, "DLX")); - args.put("x-dead-letter-routing-key", queueName); - } - return args; - } - - private Binding doRegisterConsumer(final String name, String group, MessageChannel moduleInputChannel, Queue queue, - final ExtendedConsumerProperties properties) { - DefaultBinding consumerBinding; - SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer( - this.connectionFactory); - listenerContainer.setAcknowledgeMode(properties.getExtension().getAcknowledgeMode()); - listenerContainer.setChannelTransacted(properties.getExtension().isTransacted()); - listenerContainer.setDefaultRequeueRejected(properties.getExtension().isRequeueRejected()); - int concurrency = properties.getConcurrency(); - concurrency = concurrency > 0 ? concurrency : 1; - listenerContainer.setConcurrentConsumers(concurrency); - int maxConcurrency = properties.getExtension().getMaxConcurrency(); - if (maxConcurrency > concurrency) { - listenerContainer.setMaxConcurrentConsumers(maxConcurrency); - } - listenerContainer.setPrefetchCount(properties.getExtension().getPrefetch()); - listenerContainer.setRecoveryInterval(properties.getExtension().getRecoveryInterval()); - listenerContainer.setTxSize(properties.getExtension().getTxSize()); - listenerContainer.setTaskExecutor(new SimpleAsyncTaskExecutor(queue.getName() + "-")); - listenerContainer.setQueues(queue); - int maxAttempts = properties.getMaxAttempts(); - if (maxAttempts > 1 || properties.getExtension().isRepublishToDlq()) { - RetryOperationsInterceptor retryInterceptor = RetryInterceptorBuilder.stateless() - .maxAttempts(maxAttempts) - .backOffOptions(properties.getBackOffInitialInterval(), - properties.getBackOffMultiplier(), - properties.getBackOffMaxInterval()) - .recoverer(determineRecoverer(name, properties.getExtension().getPrefix(), properties.getExtension().isRepublishToDlq())) - .build(); - listenerContainer.setAdviceChain(new Advice[] { retryInterceptor }); - } - listenerContainer.setAfterReceivePostProcessors(this.decompressingPostProcessor); - listenerContainer.setMessagePropertiesConverter(RabbitMessageChannelBinder.inboundMessagePropertiesConverter); - listenerContainer.afterPropertiesSet(); - AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer); - adapter.setBeanFactory(this.getBeanFactory()); - DirectChannel bridgeToModuleChannel = new DirectChannel(); - bridgeToModuleChannel.setBeanFactory(this.getBeanFactory()); - bridgeToModuleChannel.setBeanName(name + ".bridge"); - adapter.setOutputChannel(bridgeToModuleChannel); - adapter.setBeanName("inbound." + name); - DefaultAmqpHeaderMapper mapper = new DefaultAmqpHeaderMapper(); - mapper.setRequestHeaderNames(properties.getExtension().getRequestHeaderPatterns()); - mapper.setReplyHeaderNames(properties.getExtension().getReplyHeaderPatterns()); - adapter.setHeaderMapper(mapper); - adapter.afterPropertiesSet(); - consumerBinding = new DefaultBinding(name, group, moduleInputChannel, adapter) { - @Override - protected void afterUnbind() { - cleanAutoDeclareContext(properties.getExtension().getPrefix(), name); - } - }; - ReceivingHandler convertingBridge = new ReceivingHandler(); - convertingBridge.setOutputChannel(moduleInputChannel); - convertingBridge.setBeanName(name + ".convert.bridge"); - convertingBridge.afterPropertiesSet(); - bridgeToModuleChannel.subscribe(convertingBridge); - adapter.start(); - return consumerBinding; - } - - private MessageRecoverer determineRecoverer(String name, String prefix, boolean republish) { - if (republish) { - RabbitTemplate errorTemplate = new RabbitTemplate(this.connectionFactory); - RepublishMessageRecoverer republishMessageRecoverer = new RepublishMessageRecoverer(errorTemplate, - deadLetterExchangeName(prefix), - applyPrefix(prefix, name)); - return republishMessageRecoverer; - } - else { - return new RejectAndDontRequeueRecoverer(); - } - } - - private AmqpOutboundEndpoint buildOutboundEndpoint(final String name, - ExtendedProducerProperties properties, - RabbitTemplate rabbitTemplate) { - String prefix = properties.getExtension().getPrefix(); - String exchangeName = applyPrefix(prefix, name); - TopicExchange exchange = new TopicExchange(exchangeName); - declareExchange(exchangeName, exchange); - AmqpOutboundEndpoint endpoint = new AmqpOutboundEndpoint(rabbitTemplate); - endpoint.setExchangeName(exchange.getName()); - if (!properties.isPartitioned()) { - endpoint.setRoutingKey(name); - } - else { - endpoint.setExpressionRoutingKey(EXPRESSION_PARSER.parseExpression(buildPartitionRoutingExpression(name))); - } - for (String requiredGroupName : properties.getRequiredGroups()) { - String baseQueueName = exchangeName + "." + requiredGroupName; - if (!properties.isPartitioned()) { - Queue queue = new Queue(baseQueueName, true, false, false, queueArgs(baseQueueName, prefix, properties.getExtension().isAutoBindDlq())); - declareQueue(baseQueueName, queue); - autoBindDLQ(baseQueueName, baseQueueName, properties.getExtension().getPrefix(), properties.getExtension().isAutoBindDlq()); - org.springframework.amqp.core.Binding binding = BindingBuilder.bind(queue).to(exchange).with(name); - declareBinding(baseQueueName, binding); - } - else { - // if the stream is partitioned, create one queue for each target partition for the default group - for (int i = 0; i < properties.getPartitionCount(); i++) { - String partitionSuffix = "-" + i; - String partitionQueueName = baseQueueName + partitionSuffix; - Queue queue = new Queue(partitionQueueName, true, false, false, - queueArgs(partitionQueueName, properties.getExtension().getPrefix(), properties.getExtension().isAutoBindDlq())); - declareQueue(queue.getName(), queue); - autoBindDLQ(baseQueueName, baseQueueName + partitionSuffix, properties.getExtension().getPrefix(), properties.getExtension().isAutoBindDlq()); - declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(name + partitionSuffix)); - } - } - } - configureOutboundHandler(endpoint, properties); - return endpoint; - } - - private void configureOutboundHandler(AmqpOutboundEndpoint handler, - ExtendedProducerProperties producerProperties) { - DefaultAmqpHeaderMapper mapper = new DefaultAmqpHeaderMapper(); - mapper.setRequestHeaderNames(producerProperties.getExtension().getRequestHeaderPatterns()); - mapper.setReplyHeaderNames(producerProperties.getExtension().getReplyHeaderPatterns()); - handler.setHeaderMapper(mapper); - handler.setDefaultDeliveryMode(producerProperties.getExtension().getDeliveryMode()); - handler.setBeanFactory(this.getBeanFactory()); - handler.afterPropertiesSet(); - } - - @Override - public Binding doBindProducer(String name, MessageChannel outputChannel, - ExtendedProducerProperties producerProperties) { - String exchangeName = applyPrefix(producerProperties.getExtension().getPrefix(), name); - TopicExchange exchange = new TopicExchange(exchangeName); - declareExchange(exchangeName, exchange); - AmqpOutboundEndpoint endpoint = this.buildOutboundEndpoint(name, producerProperties, - buildRabbitTemplate(producerProperties.getExtension())); - return doRegisterProducer(name, outputChannel, endpoint, producerProperties); - } - - private RabbitTemplate buildRabbitTemplate(RabbitProducerProperties properties) { - RabbitTemplate rabbitTemplate = null; - if (properties.isBatchingEnabled()) { - BatchingStrategy batchingStrategy = new SimpleBatchingStrategy( - properties.getBatchSize(), - properties.getBatchBufferLimit(), - properties.getBatchTimeout()); - rabbitTemplate = new BatchingRabbitTemplate(batchingStrategy, - getApplicationContext().getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, - TaskScheduler.class)); - } - else { - rabbitTemplate = new RabbitTemplate(); - } - rabbitTemplate.setConnectionFactory(this.connectionFactory); - if (properties.isCompress()) { - rabbitTemplate.setBeforePublishPostProcessors(this.compressingPostProcessor); - } - rabbitTemplate.setChannelTransacted(properties.isTransacted()); - rabbitTemplate.afterPropertiesSet(); - return rabbitTemplate; - } - - private Binding doRegisterProducer(final String name, MessageChannel moduleOutputChannel, - AmqpOutboundEndpoint delegate, ExtendedProducerProperties properties) { - return this.doRegisterProducer(name, moduleOutputChannel, delegate, null, properties); - } - - private Binding doRegisterProducer(final String name, MessageChannel moduleOutputChannel, - AmqpOutboundEndpoint delegate, String replyTo, - ExtendedProducerProperties properties) { - Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel); - MessageHandler handler = new SendingHandler(delegate, replyTo, properties); - EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel, handler); - consumer.setBeanFactory(getBeanFactory()); - consumer.setBeanName("outbound." + name); - consumer.afterPropertiesSet(); - DefaultBinding producerBinding = new DefaultBinding<>(name, null, moduleOutputChannel, consumer); - consumer.start(); - return producerBinding; - } - - /** - * If so requested, declare the DLX/DLQ and bind it. The DLQ is bound to the DLX with a routing key of the original - * queue name because we use default exchange routing by queue name for the original message. - * @param queueName The base name for the queue (including the binder prefix, if any). - * @param routingKey The routing key for the queue. - * @param autoBindDlq true if the DLQ should be bound. - */ - private void autoBindDLQ(final String queueName, String routingKey, String prefix, boolean autoBindDlq) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("autoBindDLQ=" + autoBindDlq - + " for: " + queueName); - } - if (autoBindDlq) { - String dlqName = constructDLQName(queueName); - Queue dlq = new Queue(dlqName); - declareQueue(dlqName, dlq); - final String dlxName = deadLetterExchangeName(prefix); - final DirectExchange dlx = new DirectExchange(dlxName); - declareExchange(dlxName, dlx); - declareBinding(dlqName, BindingBuilder.bind(dlq).to(dlx).with(routingKey)); - } - } - - public void declareQueue(String beanName, Queue queue) { - try { - this.rabbitAdmin.declareQueue(queue); - } - catch (AmqpConnectException e) { - if (logger.isDebugEnabled()) { - logger.debug("Declaration of queue: " + queue.getName() + " deferred - connection not available"); - } - } - addToAutoDeclareContext(beanName, queue); - } - - public void declareExchange(final String rootName, final Exchange exchange) { - try { - this.rabbitAdmin.declareExchange(exchange); - } - catch (AmqpConnectException e) { - if (logger.isDebugEnabled()) { - logger.debug("Declaration of exchange: " + exchange.getName() + " deferred - connection not available"); - } - } - addToAutoDeclareContext(rootName + ".exchange", exchange); - } - - public void declareBinding(String rootName, org.springframework.amqp.core.Binding binding) { - try { - this.rabbitAdmin.declareBinding(binding); - } - catch (AmqpConnectException e) { - if (logger.isDebugEnabled()) { - logger.debug("Declaration of binding: " + rootName + ".binding deferred - connection not available"); - } - } - addToAutoDeclareContext(rootName + ".binding", binding); - } - - private String deadLetterExchangeName(String prefix) { - return prefix + DEAD_LETTER_EXCHANGE; - } - - private void addToAutoDeclareContext(String name, Object bean) { - synchronized (this.autoDeclareContext) { - if (!this.autoDeclareContext.containsBean(name)) { - this.autoDeclareContext.getBeanFactory().registerSingleton(name, bean); - } - } - } - - public void cleanAutoDeclareContext(String prefix, String name) { - synchronized (this.autoDeclareContext) { - removeSingleton(applyPrefix(prefix, name) + ".binding"); - removeSingleton(applyPrefix(prefix, name)); - String dlq = applyPrefix(prefix, name) + ".dlq"; - removeSingleton(dlq + ".binding"); - removeSingleton(dlq); - } - } - - private void removeSingleton(String name) { - if (this.autoDeclareContext.containsBean(name)) { - ConfigurableListableBeanFactory beanFactory = this.autoDeclareContext.getBeanFactory(); - if (beanFactory instanceof DefaultListableBeanFactory) { - ((DefaultListableBeanFactory) beanFactory).destroySingleton(name); - } - } - } - - @Override - public void doManualAck(LinkedList messageHeadersList) { - Iterator iterator = messageHeadersList.iterator(); - Map channelsToAck = new HashMap<>(); - while (iterator.hasNext()) { - MessageHeaders messageHeaders = iterator.next(); - if (messageHeaders.containsKey(AmqpHeaders.CHANNEL)) { - Channel channel = (com.rabbitmq.client.Channel) messageHeaders.get(AmqpHeaders.CHANNEL); - Long deliveryTag = (Long) messageHeaders.get(AmqpHeaders.DELIVERY_TAG); - channelsToAck.put(channel, deliveryTag); - } - } - for (Map.Entry entry : channelsToAck.entrySet()) { - try { - ((Channel) entry.getKey()).basicAck(entry.getValue(), true); - } - catch (IOException e) { - this.logger.error("Exception while manually acknowledging " + e); - } - } - } - - private final class SendingHandler extends AbstractMessageHandler implements Lifecycle { - - private final MessageHandler delegate; - - private final String replyTo; - - private final ExtendedProducerProperties producerProperties; - - private final PartitionHandler partitionHandler; - - private SendingHandler(MessageHandler delegate, String replyTo, - ExtendedProducerProperties properties) { - this.delegate = delegate; - this.replyTo = replyTo; - producerProperties = properties; - ConfigurableListableBeanFactory beanFactory = RabbitMessageChannelBinder.this.getBeanFactory(); - this.setBeanFactory(beanFactory); - this.partitionHandler = new PartitionHandler(beanFactory, evaluationContext, partitionSelector, - properties); - } - - @Override - protected void handleMessageInternal(Message message) throws Exception { - MessageValues messageToSend = serializePayloadIfNecessary(message); - - if (this.replyTo != null) { - messageToSend.put(AmqpHeaders.REPLY_TO, this.replyTo); - } - if (producerProperties.isPartitioned()) { - messageToSend.put(PARTITION_HEADER, - this.partitionHandler.determinePartition(message)); - } - - this.delegate.handleMessage(messageToSend.toMessage(getMessageBuilderFactory())); - } - - @Override - public void start() { - if (this.delegate instanceof Lifecycle) { - ((Lifecycle) this.delegate).start(); - } - } - - @Override - public void stop() { - if (this.delegate instanceof Lifecycle) { - ((Lifecycle) this.delegate).stop(); - } - } - - @Override - public boolean isRunning() { - if (this.delegate instanceof Lifecycle) { - return ((Lifecycle) this.delegate).isRunning(); - } - else { - return true; - } - } - - } - - private final class ReceivingHandler extends AbstractReplyProducingMessageHandler { - - private ReceivingHandler() { - super(); - this.setBeanFactory(RabbitMessageChannelBinder.this.getBeanFactory()); - } - - @Override - protected Object handleRequestMessage(Message requestMessage) { - return deserializePayloadIfNecessary(requestMessage).toMessage(getMessageBuilderFactory()); - } - - @Override - protected boolean shouldCopyRequestHeaders() { - /* - * we've already copied the headers so no need for the ARPMH to do it, and we don't want the content-type - * restored if absent. - */ - return false; - } - - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitProducerProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitProducerProperties.java deleted file mode 100644 index 940ff586b..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/RabbitProducerProperties.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 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.rabbit; - -import javax.validation.constraints.Min; - -import org.springframework.amqp.core.MessageDeliveryMode; - -/** - * @author Marius Bogoevici - * @author Gary Russell - */ -public class RabbitProducerProperties { - - private String prefix = ""; - - private String[] requestHeaderPatterns = new String[] {"STANDARD_REQUEST_HEADERS", "*"}; - - private boolean autoBindDlq; - - private boolean compress; - - private boolean batchingEnabled; - - private int batchSize = 100; - - private int batchBufferLimit = 10000; - - private int batchTimeout = 5000; - - private boolean transacted; - - private MessageDeliveryMode deliveryMode = MessageDeliveryMode.PERSISTENT; - - private String[] replyHeaderPatterns = new String[] {"STANDARD_REPLY_HEADERS", "*"}; - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public void setRequestHeaderPatterns(String[] requestHeaderPatterns) { - this.requestHeaderPatterns = requestHeaderPatterns; - } - - public String[] getRequestHeaderPatterns() { - return requestHeaderPatterns; - } - - public void setAutoBindDlq(boolean autoBindDlq) { - this.autoBindDlq = autoBindDlq; - } - - public boolean isAutoBindDlq() { - return autoBindDlq; - } - - public void setCompress(boolean compress) { - this.compress = compress; - } - - public boolean isCompress() { - return compress; - } - - public void setDeliveryMode(MessageDeliveryMode deliveryMode) { - this.deliveryMode = deliveryMode; - } - - public MessageDeliveryMode getDeliveryMode() { - return deliveryMode; - } - - public String[] getReplyHeaderPatterns() { - return replyHeaderPatterns; - } - - public void setReplyHeaderPatterns(String[] replyHeaderPatterns) { - this.replyHeaderPatterns = replyHeaderPatterns; - } - - public boolean isBatchingEnabled() { - return batchingEnabled; - } - - public void setBatchingEnabled(boolean batchingEnabled) { - this.batchingEnabled = batchingEnabled; - } - - @Min(value = 1, message = "Batch Size should be greater than zero.") - public int getBatchSize() { - return batchSize; - } - - public void setBatchSize(int batchSize) { - this.batchSize = batchSize; - } - - @Min(value = 1, message = "Batch Buffer Limit should be greater than zero.") - public int getBatchBufferLimit() { - return batchBufferLimit; - } - - public void setBatchBufferLimit(int batchBufferLimit) { - this.batchBufferLimit = batchBufferLimit; - } - - @Min(value = 1, message = "Batch Timeout should be greater than zero.") - public int getBatchTimeout() { - return batchTimeout; - } - - public void setBatchTimeout(int batchTimeout) { - this.batchTimeout = batchTimeout; - } - - public boolean isTransacted() { - return this.transacted; - } - - public void setTransacted(boolean transacted) { - this.transacted = transacted; - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitBinderConfigurationProperties.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitBinderConfigurationProperties.java deleted file mode 100644 index 2b6cb90bb..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitBinderConfigurationProperties.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015-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.rabbit.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * @author David Turanski - */ -@ConfigurationProperties(prefix = "spring.cloud.stream.rabbit.binder") -class RabbitBinderConfigurationProperties { - - private String[] adminAddresses = new String[0]; - - private String[] nodes = new String[0]; - - private int compressionLevel; - - public String[] getAdminAddresses() { - return adminAddresses; - } - - public void setAdminAddresses(String[] adminAddresses) { - this.adminAddresses = adminAddresses; - } - - /** - * @param adminAddresses A comma-separated list of RabbitMQ management plugin URLs. - * @deprecated in favor of {@link #setAdminAddresses(String[])}. Will be removed in a - * future release. - */ - @Deprecated - public void setAdminAdresses(String[] adminAddresses) { - setAdminAddresses(adminAddresses); - } - - public String[] getNodes() { - return nodes; - } - - public void setNodes(String[] nodes) { - this.nodes = nodes; - } - - public int getCompressionLevel() { - return compressionLevel; - } - - public void setCompressionLevel(int compressionLevel) { - this.compressionLevel = compressionLevel; - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitMessageChannelBinderConfiguration.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitMessageChannelBinderConfiguration.java deleted file mode 100644 index 2ab0ed349..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitMessageChannelBinderConfiguration.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2015-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.rabbit.config; - -import org.springframework.amqp.core.MessagePostProcessor; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.support.postprocessor.DelegatingDecompressingPostProcessor; -import org.springframework.amqp.support.postprocessor.GZipPostProcessor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.stream.binder.rabbit.RabbitExtendedBindingProperties; -import org.springframework.cloud.stream.binder.rabbit.RabbitMessageChannelBinder; -import org.springframework.cloud.stream.config.codec.kryo.KryoCodecAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.integration.codec.Codec; - - -/** - * Configuration class for RabbitMQ message channel binder. - * - * @author David Turanski - */ - -@Configuration -@Import({PropertyPlaceholderAutoConfiguration.class, KryoCodecAutoConfiguration.class}) -@EnableConfigurationProperties({RabbitBinderConfigurationProperties.class, RabbitExtendedBindingProperties.class}) -public class RabbitMessageChannelBinderConfiguration { - - @Autowired - private Codec codec; - - @Autowired - private ConnectionFactory rabbitConnectionFactory; - - @Autowired - private RabbitProperties rabbitProperties; - - @Autowired - private RabbitBinderConfigurationProperties rabbitBinderConfigurationProperties; - - @Autowired - private RabbitExtendedBindingProperties rabbitExtendedBindingProperties; - - @Bean - RabbitMessageChannelBinder rabbitMessageChannelBinder() { - RabbitMessageChannelBinder binder = new RabbitMessageChannelBinder(rabbitConnectionFactory, rabbitProperties); - binder.setCodec(codec); - binder.setAdminAddresses(rabbitBinderConfigurationProperties.getAdminAddresses()); - binder.setCompressingPostProcessor(gZipPostProcessor()); - binder.setDecompressingPostProcessor(deCompressingPostProcessor()); - binder.setNodes(rabbitBinderConfigurationProperties.getNodes()); - binder.setExtendedBindingProperties(rabbitExtendedBindingProperties); - return binder; - } - - @Bean - MessagePostProcessor deCompressingPostProcessor() { - return new DelegatingDecompressingPostProcessor(); - } - - @Bean - MessagePostProcessor gZipPostProcessor() { - GZipPostProcessor gZipPostProcessor = new GZipPostProcessor(); - gZipPostProcessor.setLevel(rabbitBinderConfigurationProperties.getCompressionLevel()); - return gZipPostProcessor; - } -} - diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java deleted file mode 100644 index 34a8ece22..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/config/RabbitServiceAutoConfiguration.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2015-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.rabbit.config; - -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.RabbitHealthIndicator; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.Cloud; -import org.springframework.cloud.CloudFactory; -import org.springframework.cloud.stream.binder.Binder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Profile; - -/** - * Bind to services, either locally or in a cloud environment. - * - * @author Mark Fisher - * @author Dave Syer - * @author Glenn Renfro - * @author David Turanski - * @author Eric Bottard - * @author Marius Bogoevici - */ -@Configuration -@ConditionalOnMissingBean(Binder.class) -@Import(RabbitMessageChannelBinderConfiguration.class) -public class RabbitServiceAutoConfiguration { - - @Bean - public HealthIndicator binderHealthIndicator(RabbitTemplate rabbitTemplate) { - return new RabbitHealthIndicator(rabbitTemplate); - } - - /** - * Configuration to be used when the cloud profile is set. - */ - @Configuration - @Profile("cloud") - protected static class CloudProfile { - - /** - * Configuration to be used when the cloud profile is set, and Cloud Connectors - * are found on the classpath. - */ - @Configuration - @ConditionalOnClass(Cloud.class) - protected static class CloudConnectors { - - @Bean - public Cloud cloud() { - return new CloudFactory().getCloud(); - } - - /** - * Active only if {@code spring.cloud.stream.overrideCloudConnectors} is not - * set to {@code true}. - */ - @Configuration - @ConditionalOnProperty(value = "spring.cloud.stream.overrideCloudConnectors", havingValue = "false", matchIfMissing = true) - // Required to parse Rabbit properties which are passed to the binder for - // clustering. We need to enable it here explicitly as the default Rabbit - // configuration is not triggered. - @EnableConfigurationProperties(RabbitProperties.class) - protected static class UseCloudConnectors { - - /** - * Creates a {@link ConnectionFactory} using the singleton service - * connector. - * - * @param cloud {@link Cloud} instance to be used for accessing services. - * @return the {@link ConnectionFactory} used by the binder. - */ - @Bean - ConnectionFactory rabbitConnectionFactory(Cloud cloud) { - return cloud.getSingletonServiceConnector(ConnectionFactory.class, null); - } - - @Bean - @ConditionalOnMissingBean(RabbitTemplate.class) - RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { - return new RabbitTemplate(connectionFactory); - } - } - - /** - * Configuration to be used if - * {@code spring.cloud.stream.overrideCloudConnectors} is set to {@code true}. - * Defers to Spring Boot Autoconfiguration. - */ - @Configuration - @ConditionalOnProperty("spring.cloud.stream.overrideCloudConnectors") - @Import(RabbitAutoConfiguration.class) - protected static class OverrideCloudConnectors { - } - } - - @Configuration - @ConditionalOnMissingClass("org.springframework.cloud.Cloud") - @Import(RabbitAutoConfiguration.class) - protected static class NoCloudConnectors { - } - } - - /** - * Configuration to be used when the cloud profile is not set. Defer to Spring Boot - * autoconfiguration. - */ - @Configuration - @Profile("!cloud") - @Import(RabbitAutoConfiguration.class) - protected static class NoCloudProfile { - } -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/package-info.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/package-info.java deleted file mode 100644 index 6e65a701f..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/java/org/springframework/cloud/stream/binder/rabbit/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2015-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. - */ - -/** - * This package contains an implementation of the {@link org.springframework.cloud.stream.binder.Binder} for RabbitMQ. - */ - -package org.springframework.cloud.stream.binder.rabbit; diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/resources/META-INF/spring.binders b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/resources/META-INF/spring.binders deleted file mode 100644 index 7a65c4682..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/main/resources/META-INF/spring.binders +++ /dev/null @@ -1,2 +0,0 @@ -rabbit:\ -org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/LocalizedQueueConnectionFactoryIntegrationTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/LocalizedQueueConnectionFactoryIntegrationTests.java deleted file mode 100644 index 3d8ddc06d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/LocalizedQueueConnectionFactoryIntegrationTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015-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.rabbit; - -import java.util.UUID; - -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; - -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.LocalizedQueueConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; - -import static org.assertj.core.api.Assertions.assertThat; - - -/** - * - * @author Gary Russell - */ -public class LocalizedQueueConnectionFactoryIntegrationTests { - - @ClassRule - public static RabbitTestSupport rabbitAvailableRule = new RabbitTestSupport(true); - - private LocalizedQueueConnectionFactory lqcf; - - @Before - public void setup() { - ConnectionFactory defaultConnectionFactory = rabbitAvailableRule.getResource(); - String[] addresses = new String[] { "localhost:9999", "localhost:5672" }; - String[] adminAddresses = new String[] { "http://localhost:15672", "http://localhost:15672" }; - String[] nodes = new String[] { "foo@bar", "rabbit@localhost" }; - String vhost = "/"; - String username = "guest"; - String password = "guest"; - this.lqcf = new LocalizedQueueConnectionFactory(defaultConnectionFactory, addresses, - adminAddresses, nodes, vhost, username, password, false, null, null, null, null); - } - - @Test - public void testConnect() { - RabbitAdmin admin = new RabbitAdmin(this.lqcf); - Queue queue = new Queue(UUID.randomUUID().toString(), false, false, true); - admin.declareQueue(queue); - ConnectionFactory targetConnectionFactory = this.lqcf.getTargetConnectionFactory("[" + queue.getName() + "]"); - RabbitTemplate template = new RabbitTemplate(targetConnectionFactory); - template.convertAndSend("", queue.getName(), "foo"); - assertThat(template.receiveAndConvert(queue.getName())).isEqualTo("foo"); - ((CachingConnectionFactory) targetConnectionFactory).destroy(); - admin.deleteQueue(queue.getName()); - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderCleanerTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderCleanerTests.java deleted file mode 100644 index 715ff7fe1..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderCleanerTests.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2015-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.rabbit; - -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.DefaultConsumer; -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.ChannelCallback; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.cloud.stream.binder.AbstractBinder; -import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -/** - * @author Gary Russell - * @since 1.2 - */ -public class RabbitBinderCleanerTests { - - private static final String BINDER_PREFIX = "binder."; - - @Rule - public RabbitTestSupport rabbitWithMgmtEnabled = new RabbitTestSupport(true); - - @Test - public void testCleanStream() { - final RabbitBindingCleaner cleaner = new RabbitBindingCleaner(); - final RestTemplate template = RabbitManagementUtils.buildRestTemplate("http://localhost:15672", "guest", - "guest"); - final String stream1 = UUID.randomUUID().toString(); - String stream2 = stream1 + "-1"; - String firstQueue = null; - CachingConnectionFactory connectionFactory = rabbitWithMgmtEnabled.getResource(); - RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); - for (int i = 0; i < 5; i++) { - String queue1Name = AbstractBinder.applyPrefix(BINDER_PREFIX, stream1 + ".default." + i); - String queue2Name = AbstractBinder.applyPrefix(BINDER_PREFIX, stream2 + ".default." + i); - if (firstQueue == null) { - firstQueue = queue1Name; - } - URI uri = UriComponentsBuilder.fromUriString("http://localhost:15672/api/queues") - .pathSegment("{vhost}", "{queue}") - .buildAndExpand("/", queue1Name) - .encode().toUri(); - template.put(uri, new AmqpQueue(false, true)); - uri = UriComponentsBuilder.fromUriString("http://localhost:15672/api/queues") - .pathSegment("{vhost}", "{queue}") - .buildAndExpand("/", queue2Name) - .encode().toUri(); - template.put(uri, new AmqpQueue(false, true)); - uri = UriComponentsBuilder.fromUriString("http://localhost:15672/api/queues") - .pathSegment("{vhost}", "{queue}") - .buildAndExpand("/", AbstractBinder.constructDLQName(queue1Name)).encode().toUri(); - template.put(uri, new AmqpQueue(false, true)); - TopicExchange exchange = new TopicExchange(queue1Name); - rabbitAdmin.declareExchange(exchange); - rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(queue1Name)).to(exchange).with(queue1Name)); - exchange = new TopicExchange(queue2Name); - rabbitAdmin.declareExchange(exchange); - rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(queue2Name)).to(exchange).with(queue2Name)); - } - final TopicExchange topic1 = new TopicExchange( - AbstractBinder.applyPrefix(BINDER_PREFIX, stream1 + ".foo.bar")); - rabbitAdmin.declareExchange(topic1); - rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(firstQueue)).to(topic1).with("#")); - String foreignQueue = UUID.randomUUID().toString(); - rabbitAdmin.declareQueue(new Queue(foreignQueue)); - rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(foreignQueue)).to(topic1).with("#")); - final TopicExchange topic2 = new TopicExchange( - AbstractBinder.applyPrefix(BINDER_PREFIX, stream2 + ".foo.bar")); - rabbitAdmin.declareExchange(topic2); - rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(firstQueue)).to(topic2).with("#")); - new RabbitTemplate(connectionFactory).execute(new ChannelCallback() { - - @Override - public Void doInRabbit(Channel channel) throws Exception { - String queueName = AbstractBinder.applyPrefix(BINDER_PREFIX, stream1 + ".default." + 4); - String consumerTag = channel.basicConsume(queueName, new DefaultConsumer(channel)); - try { - waitForConsumerStateNot(queueName, 0); - cleaner.clean(stream1, false); - fail("Expected exception"); - } - catch (RabbitAdminException e) { - assertThat(e).hasMessageContaining("Queue " + queueName + " is in use"); - } - channel.basicCancel(consumerTag); - waitForConsumerStateNot(queueName, 1); - try { - cleaner.clean(stream1, false); - fail("Expected exception"); - } - catch (RabbitAdminException e) { - assertThat(e).hasMessageContaining("Cannot delete exchange "); - assertThat(e).hasMessageContaining("; it has bindings:"); - } - return null; - } - - private void waitForConsumerStateNot(String queueName, int state) throws InterruptedException { - int n = 0; - URI uri = UriComponentsBuilder.fromUriString("http://localhost:15672/api/queues").pathSegment( - "{vhost}", "{queue}") - .buildAndExpand("/", queueName).encode().toUri(); - while (n++ < 100) { - @SuppressWarnings("unchecked") - Map queueInfo = template.getForObject(uri, Map.class); - if (!queueInfo.get("consumers").equals(Integer.valueOf(state))) { - break; - } - Thread.sleep(100); - } - assertThat(n < 100).withFailMessage("Consumer state remained at " + state + " after 10 seconds"); - } - - }); - rabbitAdmin.deleteExchange(topic1.getName()); // easier than deleting the binding - rabbitAdmin.declareExchange(topic1); - rabbitAdmin.deleteQueue(foreignQueue); - connectionFactory.destroy(); - Map> cleanedMap = cleaner.clean(stream1, false); - assertThat(cleanedMap).hasSize(2); - List cleanedQueues = cleanedMap.get("queues"); - // should *not* clean stream2 - assertThat(cleanedQueues).hasSize(10); - for (int i = 0; i < 5; i++) { - assertThat(cleanedQueues.get(i * 2)).isEqualTo(BINDER_PREFIX + stream1 + ".default." + i); - assertThat(cleanedQueues.get(i * 2 + 1)).isEqualTo(BINDER_PREFIX + stream1 + ".default." + i + ".dlq"); - } - List cleanedExchanges = cleanedMap.get("exchanges"); - assertThat(cleanedExchanges).hasSize(6); - - // wild card *should* clean stream2 - cleanedMap = cleaner.clean(stream1 + "*", false); - assertThat(cleanedMap).hasSize(2); - cleanedQueues = cleanedMap.get("queues"); - assertThat(cleanedQueues).hasSize(5); - for (int i = 0; i < 5; i++) { - assertThat(cleanedQueues.get(i)).isEqualTo(BINDER_PREFIX + stream2 + ".default." + i); - } - cleanedExchanges = cleanedMap.get("exchanges"); - assertThat(cleanedExchanges).hasSize(6); - } - - public static class AmqpQueue { - - private boolean autoDelete; - - private boolean durable; - - public AmqpQueue(boolean autoDelete, boolean durable) { - this.autoDelete = autoDelete; - this.durable = durable; - } - - - @JsonProperty("auto_delete") - protected boolean isAutoDelete() { - return autoDelete; - } - - - protected void setAutoDelete(boolean autoDelete) { - this.autoDelete = autoDelete; - } - - - protected boolean isDurable() { - return durable; - } - - - protected void setDurable(boolean durable) { - this.durable = durable; - } - - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java deleted file mode 100644 index 0f828928d..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitBinderTests.java +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright 2013-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.rabbit; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.zip.Deflater; - -import org.aopalliance.aop.Advice; -import org.apache.commons.logging.Log; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import org.springframework.amqp.core.AcknowledgeMode; -import org.springframework.amqp.core.MessageDeliveryMode; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.support.AmqpHeaders; -import org.springframework.amqp.support.postprocessor.DelegatingDecompressingPostProcessor; -import org.springframework.amqp.utils.test.TestUtils; -import org.springframework.beans.DirectFieldAccessor; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.ExtendedProducerProperties; -import org.springframework.cloud.stream.binder.PartitionCapableBinderTests; -import org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy; -import org.springframework.cloud.stream.binder.PartitionSelectorStrategy; -import org.springframework.cloud.stream.binder.PartitionTestSupport; -import org.springframework.cloud.stream.binder.Spy; -import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; -import org.springframework.context.ApplicationContext; -import org.springframework.expression.spel.standard.SpelExpression; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.MessagingException; -import org.springframework.messaging.support.GenericMessage; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * @author Mark Fisher - * @author Gary Russell - * @author David Turanski - */ -public class RabbitBinderTests extends - PartitionCapableBinderTests, ExtendedProducerProperties> { - - private final String CLASS_UNDER_TEST_NAME = RabbitMessageChannelBinder.class.getSimpleName(); - - public static final String TEST_PREFIX = "bindertest."; - - @Rule - public RabbitTestSupport rabbitAvailableRule = new RabbitTestSupport(); - - @Override - protected RabbitTestBinder getBinder() { - if (testBinder == null) { - testBinder = new RabbitTestBinder(rabbitAvailableRule.getResource(), new RabbitProperties()); - } - return testBinder; - } - - @Override - protected ExtendedConsumerProperties createConsumerProperties() { - return new ExtendedConsumerProperties<>(new RabbitConsumerProperties()); - } - - @Override - protected ExtendedProducerProperties createProducerProperties() { - return new ExtendedProducerProperties<>(new RabbitProducerProperties()); - } - - @Override - protected boolean usesExplicitRouting() { - return true; - } - - @Test - public void testSendAndReceiveBad() throws Exception { - RabbitTestBinder binder = getBinder(); - DirectChannel moduleOutputChannel = new DirectChannel(); - DirectChannel moduleInputChannel = new DirectChannel(); - Binding producerBinding = binder.bindProducer("bad.0", moduleOutputChannel, - createProducerProperties()); - Binding consumerBinding = binder.bindConsumer("bad.0", "test", moduleInputChannel, - createConsumerProperties()); - Message message = MessageBuilder.withPayload("bad").setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar") - .build(); - final CountDownLatch latch = new CountDownLatch(3); - moduleInputChannel.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - latch.countDown(); - throw new RuntimeException("bad"); - } - }); - moduleOutputChannel.send(message); - assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); - producerBinding.unbind(); - consumerBinding.unbind(); - } - - @Test - public void testConsumerProperties() throws Exception { - RabbitTestBinder binder = getBinder(); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setTransacted(true); - Binding consumerBinding = binder.bindConsumer("props.0", null, new DirectChannel(), properties); - AbstractEndpoint endpoint = extractEndpoint(consumerBinding); - SimpleMessageListenerContainer container = TestUtils.getPropertyValue(endpoint, "messageListenerContainer", - SimpleMessageListenerContainer.class); - assertThat(container.getAcknowledgeMode()).isEqualTo(AcknowledgeMode.AUTO); - assertThat(container.getQueueNames()[0]).startsWith(properties.getExtension().getPrefix()); - assertThat(TestUtils.getPropertyValue(container, "transactional", Boolean.class)).isTrue(); - assertThat(TestUtils.getPropertyValue(container, "concurrentConsumers")).isEqualTo(1); - assertThat(TestUtils.getPropertyValue(container, "maxConcurrentConsumers")).isNull(); - assertThat(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class)).isTrue(); - assertThat(TestUtils.getPropertyValue(container, "prefetchCount")).isEqualTo(1); - assertThat(TestUtils.getPropertyValue(container, "txSize")).isEqualTo(1); - Advice retry = TestUtils.getPropertyValue(container, "adviceChain", Advice[].class)[0]; - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.retryPolicy.maxAttempts")).isEqualTo(3); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.initialInterval")).isEqualTo(1000L); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.maxInterval")).isEqualTo(10000L); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.multiplier")).isEqualTo(2.0); - consumerBinding.unbind(); - assertThat(endpoint.isRunning()).isFalse(); - - properties = createConsumerProperties(); - properties.getExtension().setAcknowledgeMode(AcknowledgeMode.NONE); - properties.setBackOffInitialInterval(2000); - properties.setBackOffMaxInterval(20000); - properties.setBackOffMultiplier(5.0); - properties.setConcurrency(2); - properties.setMaxAttempts(23); - properties.getExtension().setMaxConcurrency(3); - properties.getExtension().setPrefix("foo."); - properties.getExtension().setPrefetch(20); - properties.getExtension().setRequestHeaderPatterns(new String[] { "foo" }); - properties.getExtension().setRequeueRejected(false); - properties.getExtension().setTxSize(10); - properties.setInstanceIndex(0); - consumerBinding = binder.bindConsumer("props.0", "test", new DirectChannel(), properties); - - endpoint = extractEndpoint(consumerBinding); - container = verifyContainer(endpoint); - - assertThat(container.getQueueNames()[0]).isEqualTo("foo.props.0.test"); - - consumerBinding.unbind(); - assertThat(endpoint.isRunning()).isFalse(); - } - - @Test - public void testProducerProperties() throws Exception { - RabbitTestBinder binder = getBinder(); - Binding producerBinding = binder.bindProducer("props.0", new DirectChannel(), - createProducerProperties()); - @SuppressWarnings("unchecked") - AbstractEndpoint endpoint = extractEndpoint(producerBinding); - MessageDeliveryMode mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode", - MessageDeliveryMode.class); - assertThat(mode).isEqualTo(MessageDeliveryMode.PERSISTENT); - List requestHeaders = TestUtils.getPropertyValue(endpoint, - "handler.delegate.headerMapper.requestHeaderMatcher.matchers", List.class); - assertThat(requestHeaders).hasSize(2); - producerBinding.unbind(); - assertThat(endpoint.isRunning()).isFalse(); - assertThat(TestUtils.getPropertyValue(endpoint, "handler.delegate.amqpTemplate.transactional", Boolean.class)) - .isFalse(); - - ExtendedProducerProperties properties = createProducerProperties(); - properties.getExtension().setPrefix("foo."); - properties.getExtension().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); - properties.getExtension().setRequestHeaderPatterns(new String[] { "foo" }); - properties.setPartitionKeyExpression(spelExpressionParser.parseExpression("'foo'")); - properties.setPartitionKeyExtractorClass(TestPartitionKeyExtractorClass.class); - properties.setPartitionSelectorExpression(spelExpressionParser.parseExpression("0")); - properties.setPartitionSelectorClass(TestPartitionSelectorClass.class); - properties.setPartitionCount(1); - properties.getExtension().setTransacted(true); - - producerBinding = binder.bindProducer("props.0", new DirectChannel(), properties); - endpoint = extractEndpoint(producerBinding); - assertThat(TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression", SpelExpression.class) - .getExpressionString()).isEqualTo("'props.0-' + headers['partition']"); - mode = TestUtils.getPropertyValue(endpoint, "handler.delegate.defaultDeliveryMode", MessageDeliveryMode.class); - assertThat(mode).isEqualTo(MessageDeliveryMode.NON_PERSISTENT); - assertThat(TestUtils.getPropertyValue(endpoint, "handler.delegate.amqpTemplate.transactional", Boolean.class)) - .isTrue(); - verifyFooRequestProducer(endpoint); - - producerBinding.unbind(); - assertThat(endpoint.isRunning()).isFalse(); - } - - @Test - public void testDurablePubSubWithAutoBindDLQ() throws Exception { - RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource()); - - RabbitTestBinder binder = getBinder(); - - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setPrefix(TEST_PREFIX); - properties.getExtension().setAutoBindDlq(true); - properties.getExtension().setDurableSubscription(true); - properties.setMaxAttempts(1); // disable retry - properties.getExtension().setRequeueRejected(false); - DirectChannel moduleInputChannel = new DirectChannel(); - moduleInputChannel.setBeanName("durableTest"); - moduleInputChannel.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - throw new RuntimeException("foo"); - } - - }); - Binding consumerBinding = binder.bindConsumer("durabletest.0", "tgroup", moduleInputChannel, - properties); - - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.convertAndSend(TEST_PREFIX + "durabletest.0", "", "foo"); - - int n = 0; - while (n++ < 100) { - Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "durabletest.0.tgroup.dlq"); - if (deadLetter != null) { - assertThat(deadLetter).isEqualTo("foo"); - break; - } - Thread.sleep(100); - } - assertThat(n).isLessThan(100); - - consumerBinding.unbind(); - assertThat(admin.getQueueProperties(TEST_PREFIX + "durabletest.0.tgroup.dlq")).isNotNull(); - } - - @Test - public void testNonDurablePubSubWithAutoBindDLQ() throws Exception { - RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource()); - - RabbitTestBinder binder = getBinder(); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setPrefix(TEST_PREFIX); - properties.getExtension().setAutoBindDlq(true); - properties.getExtension().setDurableSubscription(false); - properties.setMaxAttempts(1); // disable retry - properties.getExtension().setRequeueRejected(false); - DirectChannel moduleInputChannel = new DirectChannel(); - moduleInputChannel.setBeanName("nondurabletest"); - moduleInputChannel.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - throw new RuntimeException("foo"); - } - - }); - Binding consumerBinding = binder.bindConsumer("nondurabletest.0", "tgroup", moduleInputChannel, - properties); - - consumerBinding.unbind(); - assertThat(admin.getQueueProperties(TEST_PREFIX + "nondurabletest.0.dlq")).isNull(); - } - - @Test - public void testAutoBindDLQ() throws Exception { - RabbitTestBinder binder = getBinder(); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setPrefix(TEST_PREFIX); - properties.getExtension().setAutoBindDlq(true); - properties.setMaxAttempts(1); // disable retry - properties.getExtension().setRequeueRejected(false); - properties.getExtension().setDurableSubscription(true); - DirectChannel moduleInputChannel = new DirectChannel(); - moduleInputChannel.setBeanName("dlqTest"); - moduleInputChannel.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - throw new RuntimeException("foo"); - } - - }); - Binding consumerBinding = binder.bindConsumer("dlqtest", "default", moduleInputChannel, - properties); - - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.convertAndSend("", TEST_PREFIX + "dlqtest.default", "foo"); - - int n = 0; - while (n++ < 100) { - Object deadLetter = template.receiveAndConvert(TEST_PREFIX + "dlqtest.default.dlq"); - if (deadLetter != null) { - assertThat(deadLetter).isEqualTo("foo"); - break; - } - Thread.sleep(100); - } - assertThat(n).isLessThan(100); - - consumerBinding.unbind(); - - ApplicationContext context = TestUtils.getPropertyValue(binder, "binder.autoDeclareContext", - ApplicationContext.class); - assertThat(context.containsBean(TEST_PREFIX + "dlqtest.default.binding")).isFalse(); - assertThat(context.containsBean(TEST_PREFIX + "dlqtest.default")).isFalse(); - assertThat(context.containsBean(TEST_PREFIX + "dlqtest.default.dlq.binding")).isFalse(); - assertThat(context.containsBean(TEST_PREFIX + "dlqtest.default.dlq")).isFalse(); - } - - @Test - public void testAutoBindDLQPartionedConsumerFirst() throws Exception { - RabbitTestBinder binder = getBinder(); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setPrefix("bindertest."); - properties.getExtension().setAutoBindDlq(true); - properties.setMaxAttempts(1); // disable retry - properties.getExtension().setRequeueRejected(false); - properties.setPartitioned(true); - properties.setInstanceIndex(0); - DirectChannel input0 = new DirectChannel(); - input0.setBeanName("test.input0DLQ"); - Binding input0Binding = binder.bindConsumer("partDLQ.0", "dlqPartGrp", input0, properties); - Binding defaultConsumerBinding1 = binder.bindConsumer("partDLQ.0", "default", - new QueueChannel(), properties); - properties.setInstanceIndex(1); - DirectChannel input1 = new DirectChannel(); - input1.setBeanName("test.input1DLQ"); - Binding input1Binding = binder.bindConsumer("partDLQ.0", "dlqPartGrp", input1, properties); - Binding defaultConsumerBinding2 = binder.bindConsumer("partDLQ.0", "default", - new QueueChannel(), properties); - - ExtendedProducerProperties producerProperties = createProducerProperties(); - producerProperties.getExtension().setPrefix("bindertest."); - producerProperties.getExtension().setAutoBindDlq(true); - producerProperties.setPartitionKeyExtractorClass(PartitionTestSupport.class); - producerProperties.setPartitionSelectorClass(PartitionTestSupport.class); - producerProperties.setPartitionCount(2); - DirectChannel output = new DirectChannel(); - output.setBeanName("test.output"); - Binding outputBinding = binder.bindProducer("partDLQ.0", output, producerProperties); - - final CountDownLatch latch0 = new CountDownLatch(1); - input0.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - if (latch0.getCount() <= 0) { - throw new RuntimeException("dlq"); - } - latch0.countDown(); - } - - }); - - final CountDownLatch latch1 = new CountDownLatch(1); - input1.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - if (latch1.getCount() <= 0) { - throw new RuntimeException("dlq"); - } - latch1.countDown(); - } - - }); - - output.send(new GenericMessage<>(1)); - assertThat(latch1.await(10, TimeUnit.SECONDS)).isTrue(); - - output.send(new GenericMessage<>(0)); - assertThat(latch0.await(10, TimeUnit.SECONDS)).isTrue(); - - output.send(new GenericMessage<>(1)); - - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.setReceiveTimeout(10000); - - String streamDLQName = "bindertest.partDLQ.0.dlqPartGrp.dlq"; - - org.springframework.amqp.core.Message received = template.receive(streamDLQName); - assertThat(received).isNotNull(); - assertThat(received.getMessageProperties().getHeaders()).containsEntry("partition", 1); - - output.send(new GenericMessage<>(0)); - received = template.receive(streamDLQName); - assertThat(received).isNotNull(); - assertThat(received.getMessageProperties().getHeaders()).containsEntry("partition", 0); - - input0Binding.unbind(); - input1Binding.unbind(); - defaultConsumerBinding1.unbind(); - defaultConsumerBinding2.unbind(); - outputBinding.unbind(); - } - - @Test - public void testAutoBindDLQPartitionedProducerFirst() throws Exception { - RabbitTestBinder binder = getBinder(); - ExtendedProducerProperties properties = createProducerProperties(); - - properties.getExtension().setPrefix("bindertest."); - properties.getExtension().setAutoBindDlq(true); - properties.setRequiredGroups("dlqPartGrp"); - properties.setPartitionKeyExtractorClass(PartitionTestSupport.class); - properties.setPartitionSelectorClass(PartitionTestSupport.class); - properties.setPartitionCount(2); - DirectChannel output = new DirectChannel(); - output.setBeanName("test.output"); - Binding outputBinding = binder.bindProducer("partDLQ.1", output, properties); - - ExtendedConsumerProperties consumerProperties = createConsumerProperties(); - consumerProperties.getExtension().setPrefix("bindertest."); - consumerProperties.getExtension().setAutoBindDlq(true); - consumerProperties.setMaxAttempts(1); // disable retry - consumerProperties.getExtension().setRequeueRejected(false); - consumerProperties.setPartitioned(true); - consumerProperties.setInstanceIndex(0); - DirectChannel input0 = new DirectChannel(); - input0.setBeanName("test.input0DLQ"); - Binding input0Binding = binder.bindConsumer("partDLQ.1", "dlqPartGrp", input0, - consumerProperties); - Binding defaultConsumerBinding1 = binder.bindConsumer("partDLQ.1", "defaultConsumer", - new QueueChannel(), consumerProperties); - consumerProperties.setInstanceIndex(1); - DirectChannel input1 = new DirectChannel(); - input1.setBeanName("test.input1DLQ"); - Binding input1Binding = binder.bindConsumer("partDLQ.1", "dlqPartGrp", input1, - consumerProperties); - Binding defaultConsumerBinding2 = binder.bindConsumer("partDLQ.1", "defaultConsumer", - new QueueChannel(), consumerProperties); - - final CountDownLatch latch0 = new CountDownLatch(1); - input0.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - if (latch0.getCount() <= 0) { - throw new RuntimeException("dlq"); - } - latch0.countDown(); - } - - }); - - final CountDownLatch latch1 = new CountDownLatch(1); - input1.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - if (latch1.getCount() <= 0) { - throw new RuntimeException("dlq"); - } - latch1.countDown(); - } - - }); - - output.send(new GenericMessage(1)); - assertThat(latch1.await(10, TimeUnit.SECONDS)).isTrue(); - - output.send(new GenericMessage(0)); - assertThat(latch0.await(10, TimeUnit.SECONDS)).isTrue(); - - output.send(new GenericMessage(1)); - - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.setReceiveTimeout(10000); - - String streamDLQName = "bindertest.partDLQ.1.dlqPartGrp.dlq"; - - org.springframework.amqp.core.Message received = template.receive(streamDLQName); - assertThat(received).isNotNull(); - assertThat(received.getMessageProperties().getHeaders()).containsEntry("partition", 1); - - output.send(new GenericMessage(0)); - received = template.receive(streamDLQName); - assertThat(received).isNotNull(); - assertThat(received.getMessageProperties().getHeaders()).containsEntry("partition", 0); - - input0Binding.unbind(); - input1Binding.unbind(); - defaultConsumerBinding1.unbind(); - defaultConsumerBinding2.unbind(); - outputBinding.unbind(); - } - - @Test - public void testAutoBindDLQwithRepublish() throws Exception { - // pre-declare the queue with dead-lettering, users can also use a policy - RabbitAdmin admin = new RabbitAdmin(this.rabbitAvailableRule.getResource()); - Map args = new HashMap(); - args.put("x-dead-letter-exchange", TEST_PREFIX + "DLX"); - args.put("x-dead-letter-routing-key", TEST_PREFIX + "dlqpubtest.default"); - Queue queue = new Queue(TEST_PREFIX + "dlqpubtest.default", true, false, false, args); - admin.declareQueue(queue); - - RabbitTestBinder binder = getBinder(); - ExtendedConsumerProperties properties = createConsumerProperties(); - properties.getExtension().setPrefix(TEST_PREFIX); - properties.getExtension().setAutoBindDlq(true); - properties.getExtension().setRepublishToDlq(true); - properties.setMaxAttempts(1); // disable retry - properties.getExtension().setRequeueRejected(false); - properties.getExtension().setDurableSubscription(true); - DirectChannel moduleInputChannel = new DirectChannel(); - moduleInputChannel.setBeanName("dlqPubTest"); - moduleInputChannel.subscribe(new MessageHandler() { - - @Override - public void handleMessage(Message message) throws MessagingException { - throw new RuntimeException("foo"); - } - - }); - Binding consumerBinding = binder.bindConsumer("dlqpubtest", "default", moduleInputChannel, - properties); - - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.convertAndSend("", TEST_PREFIX + "dlqpubtest.default", "foo"); - - int n = 0; - while (n++ < 100) { - org.springframework.amqp.core.Message deadLetter = template.receive(TEST_PREFIX + "dlqpubtest.default.dlq"); - if (deadLetter != null) { - assertThat(new String(deadLetter.getBody())).isEqualTo("foo"); - assertThat(deadLetter.getMessageProperties().getHeaders()).containsKey(("x-exception-stacktrace")); - break; - } - Thread.sleep(100); - } - assertThat(n).isLessThan(100); - - consumerBinding.unbind(); - } - - @SuppressWarnings("unchecked") - @Test - public void testBatchingAndCompression() throws Exception { - RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - RabbitTestBinder binder = getBinder(); - ExtendedProducerProperties properties = createProducerProperties(); - properties.getExtension().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); - properties.getExtension().setBatchingEnabled(true); - properties.getExtension().setBatchSize(2); - properties.getExtension().setBatchBufferLimit(100000); - properties.getExtension().setBatchTimeout(30000); - properties.getExtension().setCompress(true); - properties.setRequiredGroups("default"); - - DirectChannel output = new DirectChannel(); - output.setBeanName("batchingProducer"); - Binding producerBinding = binder.bindProducer("batching.0", output, properties); - - Log logger = spy(TestUtils.getPropertyValue(binder, "binder.compressingPostProcessor.logger", Log.class)); - new DirectFieldAccessor(TestUtils.getPropertyValue(binder, "binder.compressingPostProcessor")) - .setPropertyValue("logger", logger); - when(logger.isTraceEnabled()).thenReturn(true); - - assertThat(TestUtils.getPropertyValue(binder, "binder.compressingPostProcessor.level")) - .isEqualTo(Deflater.BEST_SPEED); - - output.send(new GenericMessage<>("foo".getBytes())); - output.send(new GenericMessage<>("bar".getBytes())); - - Object out = spyOn("batching.0.default").receive(false); - assertThat(out).isInstanceOf(byte[].class); - assertThat(new String((byte[]) out)).isEqualTo("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar"); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); - verify(logger).trace(captor.capture()); - assertThat(captor.getValue().toString()).contains(("Compressed 14 to ")); - - QueueChannel input = new QueueChannel(); - input.setBeanName("batchingConsumer"); - Binding consumerBinding = binder.bindConsumer("batching.0", "test", input, - createConsumerProperties()); - - output.send(new GenericMessage<>("foo".getBytes())); - output.send(new GenericMessage<>("bar".getBytes())); - - Message in = (Message) input.receive(10000); - assertThat(in).isNotNull(); - assertThat(new String(in.getPayload())).isEqualTo("foo"); - in = (Message) input.receive(10000); - assertThat(in).isNotNull(); - assertThat(new String(in.getPayload())).isEqualTo("bar"); - assertThat(in.getHeaders().get(AmqpHeaders.DELIVERY_MODE)).isNull(); - - producerBinding.unbind(); - consumerBinding.unbind(); - } - - /* - * Test late binding due to broker down; queues with and without DLQs, and partitioned - * queues. - */ - @Test - public void testLateBinding() throws Exception { - RabbitTestSupport.RabbitProxy proxy = new RabbitTestSupport.RabbitProxy(); - CachingConnectionFactory cf = new CachingConnectionFactory("localhost", proxy.getPort()); - RabbitMessageChannelBinder rabbitBinder = new RabbitMessageChannelBinder(cf, new RabbitProperties()); - RabbitTestBinder binder = new RabbitTestBinder(cf, rabbitBinder); - - ExtendedProducerProperties properties = createProducerProperties(); - properties.getExtension().setPrefix("latebinder."); - properties.getExtension().setAutoBindDlq(true); - - MessageChannel moduleOutputChannel = new DirectChannel(); - Binding late0ProducerBinding = binder.bindProducer("late.0", moduleOutputChannel, properties); - - QueueChannel moduleInputChannel = new QueueChannel(); - ExtendedConsumerProperties rabbitConsumerProperties = createConsumerProperties(); - rabbitConsumerProperties.getExtension().setPrefix("latebinder."); - Binding late0ConsumerBinding = binder.bindConsumer("late.0", "test", moduleInputChannel, - rabbitConsumerProperties); - - properties.setPartitionKeyExpression(spelExpressionParser.parseExpression("payload.equals('0') ? 0 : 1")); - properties.setPartitionSelectorExpression(spelExpressionParser.parseExpression("hashCode()")); - properties.setPartitionCount(2); - - MessageChannel partOutputChannel = new DirectChannel(); - Binding partlate0ProducerBinding = binder.bindProducer("partlate.0", partOutputChannel, - properties); - - QueueChannel partInputChannel0 = new QueueChannel(); - QueueChannel partInputChannel1 = new QueueChannel(); - - ExtendedConsumerProperties partLateConsumerProperties = createConsumerProperties(); - partLateConsumerProperties.getExtension().setPrefix("latebinder."); - partLateConsumerProperties.setPartitioned(true); - partLateConsumerProperties.setInstanceIndex(0); - Binding partlate0Consumer0Binding = binder.bindConsumer("partlate.0", "test", partInputChannel0, - partLateConsumerProperties); - partLateConsumerProperties.setInstanceIndex(1); - Binding partlate0Consumer1Binding = binder.bindConsumer("partlate.0", "test", partInputChannel1, - partLateConsumerProperties); - - ExtendedProducerProperties noDlqProducerProperties = createProducerProperties(); - noDlqProducerProperties.getExtension().setPrefix("latebinder."); - MessageChannel noDLQOutputChannel = new DirectChannel(); - Binding noDlqProducerBinding = binder.bindProducer("lateNoDLQ.0", noDLQOutputChannel, - noDlqProducerProperties); - - QueueChannel noDLQInputChannel = new QueueChannel(); - ExtendedConsumerProperties noDlqConsumerProperties = createConsumerProperties(); - noDlqConsumerProperties.getExtension().setPrefix("latebinder."); - Binding noDlqConsumerBinding = binder.bindConsumer("lateNoDLQ.0", "test", noDLQInputChannel, - noDlqConsumerProperties); - - MessageChannel outputChannel = new DirectChannel(); - Binding pubSubProducerBinding = binder.bindProducer("latePubSub", outputChannel, - noDlqProducerProperties); - QueueChannel pubSubInputChannel = new QueueChannel(); - noDlqConsumerProperties.getExtension().setDurableSubscription(false); - Binding nonDurableConsumerBinding = binder.bindConsumer("latePubSub", "lategroup", - pubSubInputChannel, noDlqConsumerProperties); - QueueChannel durablePubSubInputChannel = new QueueChannel(); - noDlqConsumerProperties.getExtension().setDurableSubscription(true); - Binding durableConsumerBinding = binder.bindConsumer("latePubSub", "lateDurableGroup", - durablePubSubInputChannel, noDlqConsumerProperties); - - proxy.start(); - - moduleOutputChannel.send(new GenericMessage<>("foo")); - Message message = moduleInputChannel.receive(10000); - assertThat(message).isNotNull(); - assertThat(message.getPayload()).isNotNull(); - - noDLQOutputChannel.send(new GenericMessage<>("bar")); - message = noDLQInputChannel.receive(10000); - assertThat(message); - assertThat(message.getPayload()).isEqualTo("bar"); - - outputChannel.send(new GenericMessage<>("baz")); - message = pubSubInputChannel.receive(10000); - assertThat(message); - assertThat(message.getPayload()).isEqualTo("baz"); - message = durablePubSubInputChannel.receive(10000); - assertThat(message).isNotNull(); - assertThat(message.getPayload()).isEqualTo("baz"); - - partOutputChannel.send(new GenericMessage<>("0")); - partOutputChannel.send(new GenericMessage<>("1")); - message = partInputChannel0.receive(10000); - assertThat(message).isNotNull(); - assertThat(message.getPayload()).isEqualTo("0"); - message = partInputChannel1.receive(10000); - assertThat(message).isNotNull(); - assertThat(message.getPayload()).isEqualTo("1"); - - late0ProducerBinding.unbind(); - late0ConsumerBinding.unbind(); - partlate0ProducerBinding.unbind(); - partlate0Consumer0Binding.unbind(); - partlate0Consumer1Binding.unbind(); - noDlqProducerBinding.unbind(); - noDlqConsumerBinding.unbind(); - pubSubProducerBinding.unbind(); - nonDurableConsumerBinding.unbind(); - durableConsumerBinding.unbind(); - - binder.cleanup(); - - proxy.stop(); - cf.destroy(); - - this.rabbitAvailableRule.getResource().destroy(); - } - - private SimpleMessageListenerContainer verifyContainer(AbstractEndpoint endpoint) { - SimpleMessageListenerContainer container; - Advice retry; - container = TestUtils.getPropertyValue(endpoint, "messageListenerContainer", - SimpleMessageListenerContainer.class); - assertThat(container.getAcknowledgeMode()).isEqualTo(AcknowledgeMode.NONE); - assertThat(container.getQueueNames()[0]).startsWith("foo.props.0"); - assertThat(TestUtils.getPropertyValue(container, "transactional", Boolean.class)).isFalse(); - assertThat(TestUtils.getPropertyValue(container, "concurrentConsumers")).isEqualTo(2); - assertThat(TestUtils.getPropertyValue(container, "maxConcurrentConsumers")).isEqualTo(3); - assertThat(TestUtils.getPropertyValue(container, "defaultRequeueRejected", Boolean.class)).isFalse(); - assertThat(TestUtils.getPropertyValue(container, "prefetchCount")).isEqualTo(20); - assertThat(TestUtils.getPropertyValue(container, "txSize")).isEqualTo(10); - retry = TestUtils.getPropertyValue(container, "adviceChain", Advice[].class)[0]; - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.retryPolicy.maxAttempts")).isEqualTo(23); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.initialInterval")).isEqualTo(2000L); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.maxInterval")).isEqualTo(20000L); - assertThat(TestUtils.getPropertyValue(retry, "retryOperations.backOffPolicy.multiplier")).isEqualTo(5.0); - - List requestMatchers = TestUtils.getPropertyValue(endpoint, "headerMapper.requestHeaderMatcher.matchers", - List.class); - assertThat(requestMatchers).hasSize(1); - assertThat(TestUtils.getPropertyValue(requestMatchers.get(0), "pattern")).isEqualTo("foo"); - - return container; - } - - private void verifyFooRequestProducer(AbstractEndpoint endpoint) { - List requestMatchers = TestUtils.getPropertyValue(endpoint, - "handler.delegate.headerMapper.requestHeaderMatcher.matchers", List.class); - assertThat(requestMatchers).hasSize(1); - assertThat(TestUtils.getPropertyValue(requestMatchers.get(0), "pattern")).isEqualTo("foo"); - } - - @Override - protected String getEndpointRouting(AbstractEndpoint endpoint) { - return TestUtils.getPropertyValue(endpoint, "handler.delegate.routingKeyExpression", SpelExpression.class) - .getExpressionString(); - } - - @Override - protected String getExpectedRoutingBaseDestination(String name, String group) { - return name; - } - - @Override - protected String getPubSubEndpointRouting(AbstractEndpoint endpoint) { - return TestUtils.getPropertyValue(endpoint, "handler.delegate.exchangeNameExpression", SpelExpression.class) - .getExpressionString(); - } - - @Override - protected String getClassUnderTestName() { - return CLASS_UNDER_TEST_NAME; - } - - @Override - public Spy spyOn(final String queue) { - final RabbitTemplate template = new RabbitTemplate(this.rabbitAvailableRule.getResource()); - template.setAfterReceivePostProcessors(new DelegatingDecompressingPostProcessor()); - return new Spy() { - - @Override - public Object receive(boolean expectNull) throws Exception { - if (expectNull) { - Thread.sleep(50); - return template.receiveAndConvert(new RabbitConsumerProperties().getPrefix() + queue); - } - Object bar = null; - int n = 0; - while (n++ < 100 && bar == null) { - bar = template.receiveAndConvert(new RabbitConsumerProperties().getPrefix() + queue); - Thread.sleep(100); - } - assertThat(n).isLessThan(100).withFailMessage("Message did not arrive in RabbitMQ"); - return bar; - } - - }; - } - - private static class TestPartitionKeyExtractorClass implements PartitionKeyExtractorStrategy { - - @Override - public Object extractKey(Message message) { - return null; - } - } - - private static class TestPartitionSelectorClass implements PartitionSelectorStrategy { - - @Override - public int selectPartition(Object key, int partitionCount) { - return 0; - } - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitTestBinder.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitTestBinder.java deleted file mode 100644 index 4b9e35b81..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/RabbitTestBinder.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2015-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.rabbit; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties; -import org.springframework.cloud.stream.binder.AbstractTestBinder; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.ExtendedProducerProperties; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.integration.codec.kryo.PojoCodec; -import org.springframework.integration.context.IntegrationContextUtils; -import org.springframework.messaging.MessageChannel; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -/** - * Test support class for {@link RabbitMessageChannelBinder}. - * - * @author Ilayaperumal Gopinathan - * @author Gary Russell - * @author David Turanski - * @author Mark Fisher - */ -public class RabbitTestBinder extends AbstractTestBinder, ExtendedProducerProperties> { - - private final RabbitAdmin rabbitAdmin; - - private final Set prefixes = new HashSet<>(); - - private final Set queues = new HashSet(); - - private final Set exchanges = new HashSet(); - - public RabbitTestBinder(ConnectionFactory connectionFactory, RabbitProperties rabbitProperties) { - this(connectionFactory, new RabbitMessageChannelBinder(connectionFactory, rabbitProperties)); - } - - public RabbitTestBinder(ConnectionFactory connectionFactory, RabbitMessageChannelBinder binder) { - GenericApplicationContext context = new GenericApplicationContext(); - ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); - scheduler.setPoolSize(1); - scheduler.afterPropertiesSet(); - context.getBeanFactory().registerSingleton(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, scheduler); - context.refresh(); - binder.setApplicationContext(context); - binder.setCodec(new PojoCodec()); - this.setBinder(binder); - this.rabbitAdmin = new RabbitAdmin(connectionFactory); - } - - @Override - public Binding bindConsumer(String name, String group, MessageChannel moduleInputChannel, - ExtendedConsumerProperties properties) { - if (group != null) { - this.queues.add(properties.getExtension().getPrefix() + name + ("." + group)); - } - this.exchanges.add(properties.getExtension().getPrefix() + name); - return super.bindConsumer(name, group, moduleInputChannel, properties); - } - - @Override - public Binding bindProducer(String name, MessageChannel moduleOutputChannel, - ExtendedProducerProperties properties) { - this.queues.add(properties.getExtension().getPrefix() + name + ".default"); - this.exchanges.add(properties.getExtension().getPrefix() + name); - return super.bindProducer(name, moduleOutputChannel, properties); - } - - @Override - public void cleanup() { - for (String queue : this.queues) { - this.rabbitAdmin.deleteQueue(queue); - this.rabbitAdmin.deleteQueue(queue + ".dlq"); - // delete any partitioned queues - for (int i = 0; i < 10; i++) { - this.rabbitAdmin.deleteQueue(queue + "-" + i); - this.rabbitAdmin.deleteQueue(queue + "-" + i + ".dlq"); - } - } - for (String exchange : this.exchanges) { - this.rabbitAdmin.deleteExchange(exchange); - } - for (String prefix : this.prefixes) { - this.rabbitAdmin.deleteExchange(prefix + "DLX"); - } - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java deleted file mode 100644 index 5ce7e402f..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/java/org/springframework/cloud/stream/binder/rabbit/integration/RabbitBinderModuleTests.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2015-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.rabbit.integration; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.junit.After; -import org.junit.ClassRule; -import org.junit.Test; -import org.mockito.Mockito; - -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.utils.test.TestUtils; -import org.springframework.beans.DirectFieldAccessor; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.health.CompositeHealthIndicator; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.Status; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.stream.annotation.EnableBinding; -import org.springframework.cloud.stream.binder.Binder; -import org.springframework.cloud.stream.binder.BinderFactory; -import org.springframework.cloud.stream.binder.Binding; -import org.springframework.cloud.stream.binder.rabbit.RabbitMessageChannelBinder; -import org.springframework.cloud.stream.binding.ChannelBindingService; -import org.springframework.cloud.stream.messaging.Processor; -import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.MessageChannel; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Marius Bogoevici - * @author Gary Russell - */ -public class RabbitBinderModuleTests { - - @ClassRule - public static RabbitTestSupport rabbitTestSupport = new RabbitTestSupport(); - - private ConfigurableApplicationContext context; - - public static final ConnectionFactory MOCK_CONNECTION_FACTORY = Mockito.mock(ConnectionFactory.class, - Mockito.RETURNS_MOCKS); - - @After - public void tearDown() { - if (context != null) { - context.close(); - context = null; - } - RabbitAdmin admin = new RabbitAdmin(rabbitTestSupport.getResource()); - admin.deleteQueue("binder.input.default"); - admin.deleteQueue("binder.output.default"); - admin.deleteExchange("binder.input"); - admin.deleteExchange("binder.output"); - } - - @Test - public void testParentConnectionFactoryInheritedByDefault() { - context = SpringApplication.run(SimpleProcessor.class, "--server.port=0"); - BinderFactory binderFactory = context.getBean(BinderFactory.class); - Binder binder = binderFactory.getBinder(null); - assertThat(binder).isInstanceOf(RabbitMessageChannelBinder.class); - DirectFieldAccessor binderFieldAccessor = new DirectFieldAccessor(binder); - ConnectionFactory binderConnectionFactory = (ConnectionFactory) binderFieldAccessor - .getPropertyValue("connectionFactory"); - assertThat(binderConnectionFactory).isInstanceOf(CachingConnectionFactory.class); - ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); - assertThat(binderConnectionFactory).isSameAs(connectionFactory); - CompositeHealthIndicator bindersHealthIndicator = context.getBean("bindersHealthIndicator", - CompositeHealthIndicator.class); - DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(bindersHealthIndicator); - assertThat(bindersHealthIndicator).isNotNull(); - @SuppressWarnings("unchecked") - Map healthIndicators = (Map) directFieldAccessor - .getPropertyValue("indicators"); - assertThat(healthIndicators).containsKey(("rabbit")); - assertThat(healthIndicators.get("rabbit").health().getStatus()).isEqualTo((Status.UP)); - } - - @Test - @SuppressWarnings("unchecked") - public void testParentConnectionFactoryInheritedByDefaultAndRabbitSettingsPropagated() { - context = SpringApplication.run(SimpleProcessor.class, "--server.port=0", - "--spring.cloud.stream.rabbit.bindings.input.consumer.transacted=true", - "--spring.cloud.stream.rabbit.bindings.output.producer.transacted=true"); - BinderFactory binderFactory = context.getBean(BinderFactory.class); - Binder binder = binderFactory.getBinder(null); - assertThat(binder).isInstanceOf(RabbitMessageChannelBinder.class); - ChannelBindingService channelBindingService = context.getBean(ChannelBindingService.class); - DirectFieldAccessor channelBindingServiceAccessor = new DirectFieldAccessor(channelBindingService); - Map>> consumerBindings = (Map>>) channelBindingServiceAccessor - .getPropertyValue("consumerBindings"); - Binding inputBinding = consumerBindings.get("input").get(0); - SimpleMessageListenerContainer container = TestUtils.getPropertyValue(inputBinding, - "endpoint.messageListenerContainer", SimpleMessageListenerContainer.class); - assertThat(TestUtils.getPropertyValue(container, "transactional", Boolean.class)).isTrue(); - Map> producerBindings = (Map>) TestUtils - .getPropertyValue(channelBindingService, "producerBindings"); - Binding outputBinding = producerBindings.get("output"); - assertThat(TestUtils.getPropertyValue(outputBinding, "endpoint.handler.delegate.amqpTemplate.transactional", - Boolean.class)).isTrue(); - DirectFieldAccessor binderFieldAccessor = new DirectFieldAccessor(binder); - ConnectionFactory binderConnectionFactory = (ConnectionFactory) binderFieldAccessor - .getPropertyValue("connectionFactory"); - assertThat(binderConnectionFactory).isInstanceOf(CachingConnectionFactory.class); - ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); - assertThat(binderConnectionFactory).isSameAs(connectionFactory); - CompositeHealthIndicator bindersHealthIndicator = context.getBean("bindersHealthIndicator", - CompositeHealthIndicator.class); - DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(bindersHealthIndicator); - assertThat(bindersHealthIndicator).isNotNull(); - @SuppressWarnings("unchecked") - Map healthIndicators = (Map) directFieldAccessor - .getPropertyValue("indicators"); - assertThat(healthIndicators).containsKey("rabbit"); - assertThat(healthIndicators.get("rabbit").health().getStatus()).isEqualTo(Status.UP); - } - - @Test - public void testParentConnectionFactoryInheritedIfOverridden() { - context = new SpringApplication(SimpleProcessor.class, ConnectionFactoryConfiguration.class) - .run("--server.port=0"); - BinderFactory binderFactory = context.getBean(BinderFactory.class); - Binder binder = binderFactory.getBinder(null); - assertThat(binder).isInstanceOf(RabbitMessageChannelBinder.class); - DirectFieldAccessor binderFieldAccessor = new DirectFieldAccessor(binder); - ConnectionFactory binderConnectionFactory = (ConnectionFactory) binderFieldAccessor - .getPropertyValue("connectionFactory"); - assertThat(binderConnectionFactory).isSameAs(MOCK_CONNECTION_FACTORY); - ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); - assertThat(binderConnectionFactory).isSameAs(connectionFactory); - CompositeHealthIndicator bindersHealthIndicator = context.getBean("bindersHealthIndicator", - CompositeHealthIndicator.class); - assertThat(bindersHealthIndicator).isNotNull(); - DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(bindersHealthIndicator); - @SuppressWarnings("unchecked") - Map healthIndicators = (Map) directFieldAccessor - .getPropertyValue("indicators"); - assertThat(healthIndicators).containsKey("rabbit"); - // mock connection factory behaves as if down - assertThat(healthIndicators.get("rabbit").health().getStatus()).isEqualTo(Status.DOWN); - } - - @Test - public void testParentConnectionFactoryNotInheritedByCustomizedBinders() { - List params = new ArrayList<>(); - params.add("--spring.cloud.stream.input.binder=custom"); - params.add("--spring.cloud.stream.output.binder=custom"); - params.add("--spring.cloud.stream.binders.custom.type=rabbit"); - params.add("--spring.cloud.stream.binders.custom.environment.foo=bar"); - params.add("--server.port=0"); - context = SpringApplication.run(SimpleProcessor.class, params.toArray(new String[params.size()])); - BinderFactory binderFactory = context.getBean(BinderFactory.class); - Binder binder = binderFactory.getBinder(null); - assertThat(binder).isInstanceOf(RabbitMessageChannelBinder.class); - DirectFieldAccessor binderFieldAccessor = new DirectFieldAccessor(binder); - ConnectionFactory binderConnectionFactory = (ConnectionFactory) binderFieldAccessor - .getPropertyValue("connectionFactory"); - ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); - assertThat(binderConnectionFactory).isNotSameAs(connectionFactory); - CompositeHealthIndicator bindersHealthIndicator = context.getBean("bindersHealthIndicator", - CompositeHealthIndicator.class); - assertThat(bindersHealthIndicator); - DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(bindersHealthIndicator); - @SuppressWarnings("unchecked") - Map healthIndicators = (Map) directFieldAccessor - .getPropertyValue("indicators"); - assertThat(healthIndicators).containsKey("custom"); - assertThat(healthIndicators.get("custom").health().getStatus()).isEqualTo(Status.UP); - } - - @EnableBinding(Processor.class) - @SpringBootApplication - public static class SimpleProcessor { - - } - - public static class ConnectionFactoryConfiguration { - - @Bean - public ConnectionFactory connectionFactory() { - return MOCK_CONNECTION_FACTORY; - } - - } - -} diff --git a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/resources/log4j.properties b/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/resources/log4j.properties deleted file mode 100644 index 335956bc0..000000000 --- a/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit/src/test/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -log4j.rootCategory=DEBUG, stdout - -# standard logging including calling site -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n - -log4j.category.org.springframework.cloud.binder.rabbit=DEBUG