Support flexible bound element types
Fixes #519 Introduces some internal changes to the framework allowing the use of other types than MessageChannel/SubscribableChannel as bindable types (e.g. Flux, Observable, KStream, etc.) - Modify BinderFactory to allow the retrieval and lookup of a binder not only by name, but also by binding target type - Subsequent changes to ChannelBindingService and tests to account for the modified signature - Introduce BindingTargetFactory as the contract for creating bound elements - Remove any references to chanels and bound elements and use 'binding target' systematically across the board Reinstate our own Checkstyle checks with a reduced set of rules so that header validation can be performed automatically at compile time. Use ${project.version} for the checkstyle plugin configuration Renaming some occurences of 'boundElement' to 'bindingTarget' Renamed a stray occurence of 'channel' Fix some occurences of String concatenation in the same line after reformatting
This commit is contained in:
committed by
markfisher
parent
8e5689c298
commit
8ac71c7b57
491
pom.xml
491
pom.xml
@@ -1,246 +1,249 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-parent</artifactId>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.2.1.RELEASE</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-stream</url>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-stream.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-stream.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>1.7</java.version>
|
||||
<rxjava.version>1.1.10</rxjava.version>
|
||||
<spring.tuple.version>1.0.0.RELEASE</spring.tuple.version>
|
||||
<spring.integration.tuple.version>1.0.0.RELEASE</spring.integration.tuple.version>
|
||||
<reactor.version>3.0.2.RELEASE</reactor.version>
|
||||
<kryo-shaded.version>3.0.3</kryo-shaded.version>
|
||||
<objenesis.version>2.1</objenesis.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-codec</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-rxjava</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-schema-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-binder-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tuple</artifactId>
|
||||
<version>${spring.tuple.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-tuple</artifactId>
|
||||
<version>${spring.integration.tuple.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support-internal</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.esotericsoftware</groupId>
|
||||
<artifactId>kryo-shaded</artifactId>
|
||||
<version>${kryo-shaded.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<version>${rxjava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.objenesis</groupId>
|
||||
<artifactId>objenesis</artifactId>
|
||||
<version>${objenesis.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<modules>
|
||||
<module>spring-cloud-stream</module>
|
||||
<module>spring-cloud-stream-binder-test</module>
|
||||
<module>spring-cloud-stream-codec</module>
|
||||
<module>spring-cloud-stream-rxjava</module>
|
||||
<module>spring-cloud-stream-test-support</module>
|
||||
<module>spring-cloud-stream-test-support-internal</module>
|
||||
<module>spring-cloud-stream-integration-tests</module>
|
||||
<module>spring-cloud-stream-core-docs</module>
|
||||
<module>spring-cloud-stream-reactive</module>
|
||||
<module>spring-cloud-stream-schema</module>
|
||||
<module>spring-cloud-stream-schema-server</module>
|
||||
<module>spring-cloud-stream-tools</module>
|
||||
</modules>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>7.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<quiet>true</quiet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build-tools</artifactId>
|
||||
<version>1.2.0.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle-validation</id>
|
||||
<phase>validate</phase>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<encoding>UTF-8</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spring</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-parent</artifactId>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.2.1.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<scm>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-stream</url>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-stream.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-stream.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>1.7</java.version>
|
||||
<rxjava.version>1.1.10</rxjava.version>
|
||||
<spring.tuple.version>1.0.0.RELEASE</spring.tuple.version>
|
||||
<spring.integration.tuple.version>1.0.0.RELEASE</spring.integration.tuple.version>
|
||||
<reactor.version>3.0.2.RELEASE</reactor.version>
|
||||
<kryo-shaded.version>3.0.3</kryo-shaded.version>
|
||||
<objenesis.version>2.1</objenesis.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-codec</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-rxjava</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-schema-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-binder-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tuple</artifactId>
|
||||
<version>${spring.tuple.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-tuple</artifactId>
|
||||
<version>${spring.integration.tuple.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support-internal</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.esotericsoftware</groupId>
|
||||
<artifactId>kryo-shaded</artifactId>
|
||||
<version>${kryo-shaded.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<version>${rxjava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.objenesis</groupId>
|
||||
<artifactId>objenesis</artifactId>
|
||||
<version>${objenesis.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<modules>
|
||||
<module>spring-cloud-stream</module>
|
||||
<module>spring-cloud-stream-binder-test</module>
|
||||
<module>spring-cloud-stream-codec</module>
|
||||
<module>spring-cloud-stream-rxjava</module>
|
||||
<module>spring-cloud-stream-test-support</module>
|
||||
<module>spring-cloud-stream-test-support-internal</module>
|
||||
<module>spring-cloud-stream-integration-tests</module>
|
||||
<module>spring-cloud-stream-core-docs</module>
|
||||
<module>spring-cloud-stream-reactive</module>
|
||||
<module>spring-cloud-stream-schema</module>
|
||||
<module>spring-cloud-stream-schema-server</module>
|
||||
<module>spring-cloud-stream-tools</module>
|
||||
</modules>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>7.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<quiet>true</quiet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle-validation</id>
|
||||
<phase>validate</phase>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<headerLocation>checkstyle-header.txt</headerLocation>
|
||||
<suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation>
|
||||
<encoding>UTF-8</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spring</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
@@ -233,17 +233,17 @@ public abstract class AbstractBinderTests<B extends AbstractTestBinder<? extends
|
||||
|
||||
protected DirectChannel createBindableChannel(String channelName, BindingProperties bindingProperties) throws
|
||||
Exception {
|
||||
ChannelBindingServiceProperties channelBindingServiceProperties = new ChannelBindingServiceProperties();
|
||||
channelBindingServiceProperties.getBindings().put(channelName, bindingProperties);
|
||||
BindingServiceProperties bindingServiceProperties = new BindingServiceProperties();
|
||||
bindingServiceProperties.getBindings().put(channelName, bindingProperties);
|
||||
ConfigurableApplicationContext applicationContext = new GenericApplicationContext();
|
||||
applicationContext.refresh();
|
||||
channelBindingServiceProperties.setApplicationContext(applicationContext);
|
||||
channelBindingServiceProperties.setConversionService(new DefaultConversionService());
|
||||
channelBindingServiceProperties.afterPropertiesSet();
|
||||
bindingServiceProperties.setApplicationContext(applicationContext);
|
||||
bindingServiceProperties.setConversionService(new DefaultConversionService());
|
||||
bindingServiceProperties.afterPropertiesSet();
|
||||
DirectChannel channel = new DirectChannel();
|
||||
channel.setBeanName(channelName);
|
||||
MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
|
||||
channelBindingServiceProperties,
|
||||
bindingServiceProperties,
|
||||
new CompositeMessageConverterFactory(null, null));
|
||||
messageConverterConfigurer.setBeanFactory(applicationContext.getBeanFactory());
|
||||
messageConverterConfigurer.afterPropertiesSet();
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.cloud.stream.test.binder.TestSupportBinder;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -53,7 +54,7 @@ public class ContentTypeOutboundSourceTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMessageHeaderWhenNoExplicitContentTypeOnMessage() throws Exception {
|
||||
testSource.output().send(MessageBuilder.withPayload("{\"message\":\"Hi\"}").build());
|
||||
Message<String> received = (Message<String>) ((TestSupportBinder) binderFactory.getBinder(null))
|
||||
Message<String> received = (Message<String>) ((TestSupportBinder) binderFactory.getBinder(null, MessageChannel.class))
|
||||
.messageCollector().forChannel(testSource.output()).poll();
|
||||
assertThat(received.getHeaders().get(MessageHeaders.CONTENT_TYPE).toString()).isEqualTo("application/json");
|
||||
assertThat(received).hasFieldOrPropertyWithValue("payload", "{\"message\":\"Hi\"}");
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.AbstractMessageConverter;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
@@ -70,7 +71,7 @@ public class CustomMessageConverterTests {
|
||||
BarConverter.class, DefaultDatatypeChannelMessageConverter.class);
|
||||
testSource.output().send(MessageBuilder.withPayload(new Foo("hi")).build());
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<String> received = (Message<String>) ((TestSupportBinder) binderFactory.getBinder(null))
|
||||
Message<String> received = (Message<String>) ((TestSupportBinder) binderFactory.getBinder(null, MessageChannel.class))
|
||||
.messageCollector().forChannel(testSource.output()).poll(1, TimeUnit.SECONDS);
|
||||
Assert.assertThat(received, notNullValue());
|
||||
assertThat(received.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeType.valueOf("test/foo"));
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -59,7 +60,7 @@ public class DeserializeJSONToJavaTypeTests {
|
||||
public void testMessageDeserialized() throws Exception {
|
||||
testProcessor.input().send(MessageBuilder.withPayload("{\"name\":\"Bar\"}").setHeader("contentType", "application/json").build());
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<?> received = ((TestSupportBinder) binderFactory.getBinder(null))
|
||||
Message<?> received = ((TestSupportBinder) binderFactory.getBinder(null, MessageChannel.class))
|
||||
.messageCollector().forChannel(testProcessor.output()).poll(1, TimeUnit.SECONDS);
|
||||
assertThat(received).isNotNull();
|
||||
assertThat(received.getPayload()).isInstanceOf(Foo.class);
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -55,7 +56,7 @@ public class ErrorChannelTests {
|
||||
|
||||
@Test
|
||||
public void testErrorChannelBinding() throws Exception {
|
||||
Message<?> message = ((TestSupportBinder) binderFactory.getBinder(null)).messageCollector().forChannel(errorChannel).poll(10, TimeUnit.SECONDS);
|
||||
Message<?> message = ((TestSupportBinder) binderFactory.getBinder(null, MessageChannel.class)).messageCollector().forChannel(errorChannel).poll(10, TimeUnit.SECONDS);
|
||||
Assert.isTrue(message instanceof ErrorMessage, "Message should be an instance of ErrorMessage");
|
||||
Assert.isTrue(message.getPayload() instanceof MessagingException, "Message payload should be an instance" +
|
||||
"of MessagingException");
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -208,9 +208,9 @@ public class StreamListenerHandlerMethodTests {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
processor.input().send(MessageBuilder.withPayload("{\"foo\":\"fooTESTing" + "\"}")
|
||||
processor.input().send(MessageBuilder.withPayload("{\"foo\":\"fooTESTing\"}")
|
||||
.setHeader("contentType", "application/json").build());
|
||||
inboundChannel2.input().send(MessageBuilder.withPayload("{\"bar\":\"bartestING" + "\"}")
|
||||
inboundChannel2.input().send(MessageBuilder.withPayload("{\"bar\":\"bartestING\"}")
|
||||
.setHeader("contentType", "application/json").build());
|
||||
assertThat(latch.await(1, TimeUnit.SECONDS));
|
||||
context.close();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
@@ -53,7 +54,7 @@ public class StreamListenerTestUtils {
|
||||
|
||||
public interface FooInboundChannel1 {
|
||||
|
||||
public String INPUT = "foo1-input";
|
||||
String INPUT = "foo1-input";
|
||||
|
||||
@Input(FooInboundChannel1.INPUT)
|
||||
SubscribableChannel input();
|
||||
|
||||
@@ -20,18 +20,19 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Used for {@link org.springframework.cloud.stream.annotation.StreamListener} arguments annotated with {@link
|
||||
* org.springframework.cloud.stream.annotation.Output}.
|
||||
* Used for {@link org.springframework.cloud.stream.annotation.StreamListener} arguments
|
||||
* annotated with {@link org.springframework.cloud.stream.annotation.Output}.
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface FluxSender {
|
||||
|
||||
/**
|
||||
* Streams the {@link reactor.core.publisher.Flux} through the bound
|
||||
* element corresponding to the {@link org.springframework.cloud.stream.annotation.Output} annotation of the
|
||||
* argument.
|
||||
* @param flux a {@link Flux} that will be streamed through the bound element
|
||||
* @return a {@link Mono} representing the result of sending the flux (completion or error)
|
||||
* Streams the {@link reactor.core.publisher.Flux} through the binding target
|
||||
* corresponding to the {@link org.springframework.cloud.stream.annotation.Output}
|
||||
* annotation of the argument.
|
||||
* @param flux a {@link Flux} that will be streamed through the binding target
|
||||
* @return a {@link Mono} representing the result of sending the flux (completion or
|
||||
* error)
|
||||
*/
|
||||
Mono<Void> send(Flux<?> flux);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@ public class FluxToMessageChannelResultAdapter
|
||||
private Log log = LogFactory.getLog(FluxToMessageChannelResultAdapter.class);
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> resultType, Class<?> boundType) {
|
||||
return Flux.class.isAssignableFrom(resultType) && MessageChannel.class.isAssignableFrom(boundType);
|
||||
public boolean supports(Class<?> resultType, Class<?> bindingTarget) {
|
||||
return Flux.class.isAssignableFrom(resultType) && MessageChannel.class.isAssignableFrom(bindingTarget);
|
||||
}
|
||||
|
||||
public void adapt(Flux<?> streamListenerResult, MessageChannel boundElement) {
|
||||
public void adapt(Flux<?> streamListenerResult, MessageChannel bindingTarget) {
|
||||
streamListenerResult
|
||||
.doOnError(e -> this.log.error("Error while processing result", e))
|
||||
.retry()
|
||||
.subscribe(
|
||||
result -> boundElement.send(result instanceof Message<?> ? (Message<?>) result
|
||||
result -> bindingTarget.send(result instanceof Message<?> ? (Message<?>) result
|
||||
: MessageBuilder.withPayload(result).build()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ public class MessageChannelToFluxSenderParameterAdapter
|
||||
private Log log = LogFactory.getLog(MessageChannelToFluxSenderParameterAdapter.class);
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> boundElementType, MethodParameter methodParameter) {
|
||||
public boolean supports(Class<?> bindingTargetType, MethodParameter methodParameter) {
|
||||
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
|
||||
return MessageChannel.class.isAssignableFrom(boundElementType)
|
||||
return MessageChannel.class.isAssignableFrom(bindingTargetType)
|
||||
&& FluxSender.class.isAssignableFrom(type.getRawClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluxSender adapt(MessageChannel boundElement, MethodParameter parameter) {
|
||||
public FluxSender adapt(MessageChannel bindingTarget, MethodParameter parameter) {
|
||||
return resultPublisher -> {
|
||||
MonoProcessor<Void> sendResult = MonoProcessor.create();
|
||||
// add error handling and reconnect in the event of an error
|
||||
@@ -53,7 +53,7 @@ public class MessageChannelToFluxSenderParameterAdapter
|
||||
.doOnError(e -> this.log.error("Error during processing: ", e))
|
||||
.retry()
|
||||
.subscribe(
|
||||
result -> boundElement.send(result instanceof Message<?> ? (Message<?>) result :
|
||||
result -> bindingTarget.send(result instanceof Message<?> ? (Message<?>) result :
|
||||
MessageBuilder.withPayload(result).build()), e -> sendResult.onError(e),
|
||||
() -> sendResult.onComplete());
|
||||
return sendResult;
|
||||
|
||||
@@ -45,13 +45,13 @@ public class MessageChannelToInputFluxParameterAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> boundElementType, MethodParameter methodParameter) {
|
||||
return SubscribableChannel.class.isAssignableFrom(boundElementType)
|
||||
public boolean supports(Class<?> bindingTargetType, MethodParameter methodParameter) {
|
||||
return SubscribableChannel.class.isAssignableFrom(bindingTargetType)
|
||||
&& Flux.class.isAssignableFrom(methodParameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<?> adapt(final SubscribableChannel boundElement, MethodParameter parameter) {
|
||||
public Flux<?> adapt(final SubscribableChannel bindingTarget, MethodParameter parameter) {
|
||||
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
|
||||
final Class<?> argumentClass = (resolvableType.getGeneric(0).getRawClass() != null) ? (resolvableType
|
||||
.getGeneric(0).getRawClass()) : Object.class;
|
||||
@@ -63,8 +63,8 @@ public class MessageChannelToInputFluxParameterAdapter
|
||||
emitter.next(message);
|
||||
}
|
||||
};
|
||||
boundElement.subscribe(messageHandler);
|
||||
emitter.setCancellation(() -> boundElement.unsubscribe(messageHandler));
|
||||
bindingTarget.subscribe(messageHandler);
|
||||
emitter.setCancellation(() -> bindingTarget.unsubscribe(messageHandler));
|
||||
}).publish().autoConnect();
|
||||
}
|
||||
else {
|
||||
@@ -80,8 +80,8 @@ public class MessageChannelToInputFluxParameterAdapter
|
||||
}
|
||||
}
|
||||
};
|
||||
boundElement.subscribe(messageHandler);
|
||||
emitter.setCancellation(() -> boundElement.unsubscribe(messageHandler));
|
||||
bindingTarget.subscribe(messageHandler);
|
||||
emitter.setCancellation(() -> bindingTarget.unsubscribe(messageHandler));
|
||||
}).publish().autoConnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +41,14 @@ public class MessageChannelToInputObservableParameterAdapter
|
||||
this.messageChannelToInputFluxArgumentAdapter = messageChannelToInputFluxArgumentAdapter;
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> boundElementType, MethodParameter methodParameter) {
|
||||
return SubscribableChannel.class.isAssignableFrom(boundElementType)
|
||||
public boolean supports(Class<?> bindingTargetType, MethodParameter methodParameter) {
|
||||
return SubscribableChannel.class.isAssignableFrom(bindingTargetType)
|
||||
&& Observable.class.isAssignableFrom(methodParameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<?> adapt(final SubscribableChannel boundElement, MethodParameter parameter) {
|
||||
public Observable<?> adapt(final SubscribableChannel bindingTarget, MethodParameter parameter) {
|
||||
return RxJava1Adapter.publisherToObservable(
|
||||
this.messageChannelToInputFluxArgumentAdapter.adapt(boundElement, parameter));
|
||||
this.messageChannelToInputFluxArgumentAdapter.adapt(bindingTarget, parameter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,19 +43,19 @@ public class MessageChannelToObservableSenderParameterAdapter implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> boundElementType, MethodParameter methodParameter) {
|
||||
public boolean supports(Class<?> bindingTargetType, MethodParameter methodParameter) {
|
||||
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
|
||||
return MessageChannel.class.isAssignableFrom(boundElementType)
|
||||
return MessageChannel.class.isAssignableFrom(bindingTargetType)
|
||||
&& ObservableSender.class.isAssignableFrom(type.getRawClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableSender adapt(MessageChannel boundElement, MethodParameter parameter) {
|
||||
public ObservableSender adapt(MessageChannel bindingTarget, MethodParameter parameter) {
|
||||
return new ObservableSender() {
|
||||
|
||||
private FluxSender fluxSender = MessageChannelToObservableSenderParameterAdapter.this
|
||||
.messageChannelToFluxSenderArgumentAdapter
|
||||
.adapt(boundElement, parameter);
|
||||
.adapt(bindingTarget, parameter);
|
||||
|
||||
@Override
|
||||
public Single<Void> send(Observable<?> observable) {
|
||||
|
||||
@@ -20,17 +20,20 @@ import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
/**
|
||||
* Used for {@link org.springframework.cloud.stream.annotation.StreamListener} arguments annotated with {@link
|
||||
* org.springframework.cloud.stream.annotation.Output}.
|
||||
* Used for {@link org.springframework.cloud.stream.annotation.StreamListener} arguments
|
||||
* annotated with {@link org.springframework.cloud.stream.annotation.Output}.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface ObservableSender {
|
||||
|
||||
/**
|
||||
* Streams the {@link Observable} through the bound
|
||||
* element corresponding to the {@link org.springframework.cloud.stream.annotation.Output} annotation of the
|
||||
* Streams the {@link Observable} through the binding target corresponding to the
|
||||
* {@link org.springframework.cloud.stream.annotation.Output} annotation of the
|
||||
* argument.
|
||||
* @param observable an {@link Observable} that will be streamed through the bound element
|
||||
*
|
||||
* @param observable an {@link Observable} that will be streamed through the bound
|
||||
* element
|
||||
* @return a {@link Single} representing the result of an operation
|
||||
*/
|
||||
Single<Void> send(Observable<?> observable);
|
||||
|
||||
@@ -40,13 +40,13 @@ public class ObservableToMessageChannelResultAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> resultType, Class<?> boundType) {
|
||||
public boolean supports(Class<?> resultType, Class<?> bindingTarget) {
|
||||
return Observable.class.isAssignableFrom(resultType)
|
||||
&& MessageChannel.class.isAssignableFrom(boundType);
|
||||
&& MessageChannel.class.isAssignableFrom(bindingTarget);
|
||||
}
|
||||
|
||||
public void adapt(Observable<?> streamListenerResult, MessageChannel boundElement) {
|
||||
public void adapt(Observable<?> streamListenerResult, MessageChannel bindingTarget) {
|
||||
this.fluxToMessageChannelResultAdapter.adapt(RxJava1Adapter.observableToFlux(streamListenerResult),
|
||||
boundElement);
|
||||
bindingTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.reactive;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
@@ -42,6 +40,8 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
@@ -42,6 +40,8 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.stream.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -41,6 +39,8 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.stream.test.binder;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.cloud.stream.binder.Binder;
|
||||
import org.springframework.cloud.stream.binder.BinderFactory;
|
||||
import org.springframework.cloud.stream.binder.ConsumerProperties;
|
||||
import org.springframework.cloud.stream.binder.ProducerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -45,15 +47,15 @@ public class TestSupportBinderAutoConfiguration {
|
||||
public BinderFactory binderFactory() {
|
||||
return new BinderFactory() {
|
||||
@Override
|
||||
public Binder getBinder(String configurationName) {
|
||||
return messageChannelBinder;
|
||||
public <T> Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties> getBinder(String configurationName, Class<? extends T> bindableType) {
|
||||
return (Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties>) messageChannelBinder;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageCollector messageCollector(BinderFactory<MessageChannel> binderFactory) {
|
||||
return ((TestSupportBinder) binderFactory.getBinder(null)).messageCollector();
|
||||
public MessageCollector messageCollector(BinderFactory binderFactory) {
|
||||
return ((TestSupportBinder) binderFactory.getBinder(null, MessageChannel.class)).messageCollector();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.test.aggregate;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.test.aggregate;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.test.binder.MessageCollector;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -46,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class ExampleTest {
|
||||
|
||||
@Autowired
|
||||
private BinderFactory<MessageChannel> binderFactory;
|
||||
private BinderFactory binderFactory;
|
||||
|
||||
@Autowired
|
||||
private MessageCollector messageCollector;
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]org[\\/]springframework[\\/]cloud[\\/]stream[\\/]reactive[\\/]reactor[\\/]core[\\/]scheduler[\\/]" checks=".*" />
|
||||
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="AvoidStaticImport" />
|
||||
</suppressions>
|
||||
@@ -1,151 +1,164 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<!-- Root Checks -->
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="${checkstyle.header.file}" />
|
||||
<property name="fileExtensions" value="java" />
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
<!-- Root Checks -->
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="${checkstyle.header.file}"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- Annotations -->
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="elementStyle" value="compact" />
|
||||
</module>
|
||||
<module name="MissingOverride" />
|
||||
<module name="PackageAnnotation" />
|
||||
<module name="AnnotationLocation">
|
||||
<property name="allowSamelineSingleParameterlessAnnotation"
|
||||
value="false" />
|
||||
</module>
|
||||
<!-- Annotations -->
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="elementStyle" value="compact"/>
|
||||
</module>
|
||||
<module name="MissingOverride"/>
|
||||
<module name="PackageAnnotation"/>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="allowSamelineSingleParameterlessAnnotation"
|
||||
value="false"/>
|
||||
</module>
|
||||
|
||||
<!-- Block Checks -->
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text" />
|
||||
</module>
|
||||
<module name="LeftCurly" />
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone" />
|
||||
</module>
|
||||
<module name="NeedBraces" />
|
||||
<module name="AvoidNestedBlocks" />
|
||||
<!-- Block Checks -->
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text"/>
|
||||
</module>
|
||||
<!--
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
-->
|
||||
|
||||
<!-- tabs instead of spaces -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* "/>
|
||||
<property name="message" value="Indent must use tab characters"/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<!-- tabs instead of spaces -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* "/>
|
||||
<property name="message" value="Indent must use tab characters"/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Class Design -->
|
||||
<module name="FinalClass" />
|
||||
<module name="InterfaceIsType" />
|
||||
<module name="MutableException" />
|
||||
<module name="InnerTypeLast" />
|
||||
<module name="OneTopLevelClass" />
|
||||
<!-- Class Design -->
|
||||
<!--
|
||||
<module name="FinalClass"/>
|
||||
<module name="InterfaceIsType"/>
|
||||
<module name="MutableException"/>
|
||||
<module name="InnerTypeLast"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
-->
|
||||
|
||||
<!-- Coding -->
|
||||
<module name="CovariantEquals" />
|
||||
<module name="EmptyStatement" />
|
||||
<module name="EqualsHashCode" />
|
||||
<module name="InnerAssignment" />
|
||||
<module name="SimplifyBooleanExpression" />
|
||||
<module name="SimplifyBooleanReturn" />
|
||||
<module name="StringLiteralEquality" />
|
||||
<module name="NestedForDepth">
|
||||
<property name="max" value="3" />
|
||||
</module>
|
||||
<module name="NestedIfDepth">
|
||||
<property name="max" value="3" />
|
||||
</module>
|
||||
<module name="NestedTryDepth">
|
||||
<property name="max" value="3" />
|
||||
</module>
|
||||
<module name="MultipleVariableDeclarations" />
|
||||
<module name="RequireThis">
|
||||
<property name="checkMethods" value="false" />
|
||||
</module>
|
||||
<module name="OneStatementPerLine" />
|
||||
<module name="ExplicitInitialization"/>
|
||||
<module name="ParameterAssignment"/>
|
||||
<!-- Coding -->
|
||||
<!--
|
||||
<module name="CovariantEquals"/>
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="InnerAssignment"/>
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
<module name="StringLiteralEquality"/>
|
||||
<module name="NestedForDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="NestedIfDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="NestedTryDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="RequireThis">
|
||||
<property name="checkMethods" value="false"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="ExplicitInitialization"/>
|
||||
|
||||
<!-- Imports -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="AvoidStaticImport">
|
||||
<property name="excludes"
|
||||
value="org.junit.Assert.*,org.mockito.Mockito.*,org.mockito.Matchers.*,org.hamcrest.Matchers.*,org.assertj.core.api.Assertions.*"/>
|
||||
</module>
|
||||
<module name="FallThrough"/>
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="java,/^javax?\./,*,org.springframework" />
|
||||
<property name="ordered" value="true" />
|
||||
<property name="separated" value="true" />
|
||||
<property name="option" value="bottom" />
|
||||
<property name="sortStaticImportsAlphabetically" value="true" />
|
||||
</module>
|
||||
<module name="IllegalImport">
|
||||
<property name="illegalPkgs" value="org.slf4j"/>
|
||||
</module>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="0"/>
|
||||
<property name="tokens" value="CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="1"/>
|
||||
<property name="tokens" value="LAMBDA"/>
|
||||
</module>
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="3"/>
|
||||
<property name="tokens" value="METHOD_DEF"/>
|
||||
</module>
|
||||
<module name="UnusedImports"/>
|
||||
<module name="ParameterAssignment"/>
|
||||
-->
|
||||
<!-- Imports -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="AvoidStaticImport">
|
||||
<property name="excludes"
|
||||
value="org.junit.Assert.*,org.mockito.Mockito.*,org.mockito.Matchers.*,org.hamcrest.Matchers.*,org.assertj.core.api.Assertions.*"/>
|
||||
</module>
|
||||
<module name="FallThrough"/>
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="java,/^javax?\./,*,org.springframework"/>
|
||||
<property name="ordered" value="true"/>
|
||||
<property name="separated" value="true"/>
|
||||
<property name="option" value="bottom"/>
|
||||
<property name="sortStaticImportsAlphabetically" value="true"/>
|
||||
</module>
|
||||
<module name="IllegalImport">
|
||||
<property name="illegalPkgs" value="org.slf4j"/>
|
||||
</module>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="0"/>
|
||||
<property name="tokens" value="CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="1"/>
|
||||
<property name="tokens" value="LAMBDA"/>
|
||||
</module>
|
||||
<!--
|
||||
<module name="ReturnCount">
|
||||
<property name="max" value="3"/>
|
||||
<property name="tokens" value="METHOD_DEF"/>
|
||||
</module>
|
||||
-->
|
||||
<module name="UnusedImports"/>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<module name="CommentsIndentation" />
|
||||
<module name="UpperEll" />
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="OuterTypeFilename" />
|
||||
<!-- Miscellaneous -->
|
||||
<module name="CommentsIndentation"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
|
||||
<!-- Modifiers -->
|
||||
<module name="RedundantModifier" />
|
||||
<!-- Modifiers -->
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!-- Regexp -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* +\t*\S" />
|
||||
<property name="message"
|
||||
value="Line has leading space characters; indentation should be performed with tabs only." />
|
||||
<property name="ignoreComments" value="true" />
|
||||
</module>
|
||||
<module name="Regexp">
|
||||
<property name="format" value="[ \t]+$" />
|
||||
<property name="illegalPattern" value="true" />
|
||||
<property name="message" value="Trailing whitespace" />
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.junit\.Assert\.assert"/>
|
||||
<property name="message"
|
||||
value="Please use AssertJ imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<!-- Regexp -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* +\t*\S"/>
|
||||
<property name="message"
|
||||
value="Line has leading space characters; indentation should be performed with tabs only."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<!--
|
||||
<module name="Regexp">
|
||||
<property name="format" value="[ \t]+$"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
<property name="message" value="Trailing whitespace"/>
|
||||
</module>
|
||||
|
||||
<!-- Whitespace -->
|
||||
<module name="GenericWhitespace" />
|
||||
<module name="MethodParamPad" />
|
||||
<module name="NoWhitespaceAfter" >
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<module name="ParenPad" />
|
||||
<module name="TypecastParenPad" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="WhitespaceAround" />
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.junit\.Assert\.assert"/>
|
||||
<property name="message"
|
||||
value="Please use AssertJ imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
-->
|
||||
|
||||
<!-- Whitespace -->
|
||||
<!--
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceAfter">
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<module name="ParenPad"/>
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
-->
|
||||
</module>
|
||||
</module>
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.aggregate;
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,7 +77,7 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
private boolean webEnvironment = true;
|
||||
|
||||
public AggregateApplicationBuilder(String... args) {
|
||||
this(new Object[]{ ParentConfiguration.class }, args);
|
||||
this(new Object[] { ParentConfiguration.class }, args);
|
||||
}
|
||||
|
||||
public AggregateApplicationBuilder(Object source, String... args) {
|
||||
@@ -184,8 +184,7 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
Class<?> appToEmbed = appConfigurer.getApp();
|
||||
// Always update namespace before preparing SharedChannelRegistry
|
||||
if (appConfigurer.namespace == null) {
|
||||
appConfigurer.namespace = AggregateApplicationUtils.getDefaultNamespace(appConfigurer.getApp().getName(),
|
||||
i);
|
||||
appConfigurer.namespace = AggregateApplicationUtils.getDefaultNamespace(appConfigurer.getApp().getName(), i);
|
||||
}
|
||||
appsToEmbed.put(appToEmbed, appConfigurer.namespace);
|
||||
appConfigurers.put(appConfigurer, appConfigurer.namespace);
|
||||
@@ -195,18 +194,21 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
this.parentArgs.toArray(new String[0]), selfContained(), this.webEnvironment, this.headless);
|
||||
}
|
||||
else {
|
||||
if (BeanFactoryUtils.beansOfTypeIncludingAncestors(this.parentContext, SharedChannelRegistry.class)
|
||||
if (BeanFactoryUtils.beansOfTypeIncludingAncestors(this.parentContext, SharedBindingTargetRegistry.class)
|
||||
.size() == 0) {
|
||||
SharedBindingTargetRegistry sharedBindingTargetRegistry = new SharedBindingTargetRegistry();
|
||||
this.parentContext.getBeanFactory().registerSingleton("sharedBindingTargetRegistry",
|
||||
sharedBindingTargetRegistry);
|
||||
this.parentContext.getBeanFactory().registerSingleton("sharedChannelRegistry",
|
||||
new SharedChannelRegistry());
|
||||
new SharedChannelRegistry(sharedBindingTargetRegistry));
|
||||
}
|
||||
}
|
||||
SharedChannelRegistry sharedChannelRegistry = this.parentContext.getBean(SharedChannelRegistry.class);
|
||||
AggregateApplicationUtils.prepareSharedChannelRegistry(sharedChannelRegistry, appsToEmbed);
|
||||
SharedBindingTargetRegistry sharedBindingTargetRegistry = this.parentContext.getBean(SharedBindingTargetRegistry.class);
|
||||
AggregateApplicationUtils.prepareSharedBindingTargetRegistry(sharedBindingTargetRegistry, appsToEmbed);
|
||||
PropertySources propertySources = this.parentContext.getEnvironment()
|
||||
.getPropertySources();
|
||||
for (Map.Entry<AppConfigurer, String> appConfigurerEntry : appConfigurers
|
||||
.entrySet()) {
|
||||
for (Map.Entry<AppConfigurer, String> appConfigurerEntry : appConfigurers.entrySet()) {
|
||||
|
||||
AppConfigurer appConfigurer = appConfigurerEntry.getKey();
|
||||
String namespace = appConfigurerEntry.getValue().toLowerCase();
|
||||
Set<String> argsToUpdate = new LinkedHashSet<>();
|
||||
@@ -219,7 +221,8 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
// only update the values with the highest precedence level.
|
||||
if (!relaxedNameKeyExists(entry.getKey(), argKeys)) {
|
||||
String key = entry.getKey();
|
||||
// in case of environment variables pass the lower-case property key
|
||||
// in case of environment variables pass the lower-case property
|
||||
// key
|
||||
// as we pass the properties as command line properties
|
||||
if (key.contains("_")) {
|
||||
key = key.replace("_", "-").toLowerCase();
|
||||
@@ -232,8 +235,9 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
// Add the args that are set at the application level if they weren't
|
||||
// overridden above from other property sources.
|
||||
if (appConfigurer.getArgs() != null) {
|
||||
for (String arg: appConfigurer.getArgs()) {
|
||||
// use the key part left to the assignment and trimming the prefix `--`
|
||||
for (String arg : appConfigurer.getArgs()) {
|
||||
// use the key part left to the assignment and trimming the prefix
|
||||
// `--`
|
||||
String key = arg.substring(0, arg.indexOf("=")).substring(2);
|
||||
if (!relaxedNameKeyExists(key, argKeys)) {
|
||||
argsToUpdate.add(arg);
|
||||
@@ -269,10 +273,9 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
return false;
|
||||
}
|
||||
|
||||
private ChildContextBuilder childContext(Class<?> app,
|
||||
ConfigurableApplicationContext parentContext, String namespace) {
|
||||
return new ChildContextBuilder(AggregateApplicationUtils.embedApp(parentContext,
|
||||
namespace, app));
|
||||
private ChildContextBuilder childContext(Class<?> app, ConfigurableApplicationContext parentContext,
|
||||
String namespace) {
|
||||
return new ChildContextBuilder(AggregateApplicationUtils.embedApp(parentContext, namespace, app));
|
||||
}
|
||||
|
||||
public class SourceConfigurer extends AppConfigurer<SourceConfigurer> {
|
||||
@@ -364,9 +367,8 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
}
|
||||
|
||||
void embed() {
|
||||
final ConfigurableApplicationContext childContext =
|
||||
childContext(this.app, AggregateApplicationBuilder.this.parentContext,
|
||||
this.namespace).args(this.args).config(this.names)
|
||||
final ConfigurableApplicationContext childContext = childContext(this.app,
|
||||
AggregateApplicationBuilder.this.parentContext, this.namespace).args(this.args).config(this.names)
|
||||
.profiles(this.profiles).run();
|
||||
// Register bindable proxies as beans so they can be queried for later
|
||||
Map<String, BindableProxyFactory> bindableProxies = BeanFactoryUtils
|
||||
@@ -456,10 +458,16 @@ public class AggregateApplicationBuilder implements AggregateApplication, Applic
|
||||
@EnableAutoConfiguration
|
||||
@EnableBinding
|
||||
public static class ParentConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SharedBindingTargetRegistry.class)
|
||||
public SharedBindingTargetRegistry sharedBindingTargetRegistry() {
|
||||
return new SharedBindingTargetRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SharedChannelRegistry.class)
|
||||
public SharedChannelRegistry sharedChannelRegistry() {
|
||||
return new SharedChannelRegistry();
|
||||
public SharedChannelRegistry sharedChannelRegistry(SharedBindingTargetRegistry sharedBindingTargetRegistry) {
|
||||
return new SharedChannelRegistry(sharedBindingTargetRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.Banner.Mode;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.stream.internal.InternalPropertyNames;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
@@ -34,29 +35,20 @@ import org.springframework.messaging.SubscribableChannel;
|
||||
*/
|
||||
abstract class AggregateApplicationUtils {
|
||||
|
||||
private static final String SPRING_CLOUD_STREAM_INTERNAL_PREFIX = "spring.cloud.stream.internal";
|
||||
public static final String INPUT_BINDING_NAME = "input";
|
||||
|
||||
private static final String CHANNEL_NAMESPACE_PROPERTY_NAME =
|
||||
SPRING_CLOUD_STREAM_INTERNAL_PREFIX + ".channelNamespace";
|
||||
public static final String OUTPUT_BINDING_NAME = "output";
|
||||
|
||||
private static final String SELF_CONTAINED_APP_PROPERTY_NAME =
|
||||
SPRING_CLOUD_STREAM_INTERNAL_PREFIX + ".selfContained";
|
||||
|
||||
public static final String INPUT_CHANNEL_NAME = "input";
|
||||
|
||||
public static final String OUTPUT_CHANNEL_NAME = "output";
|
||||
|
||||
static ConfigurableApplicationContext createParentContext(Object[] sources, String[] args, final
|
||||
boolean selfContained, boolean webEnvironment,
|
||||
static ConfigurableApplicationContext createParentContext(Object[] sources,
|
||||
String[] args, final boolean selfContained, boolean webEnvironment,
|
||||
boolean headless) {
|
||||
SpringApplicationBuilder aggregatorParentConfiguration = new SpringApplicationBuilder();
|
||||
aggregatorParentConfiguration
|
||||
.sources(sources)
|
||||
.web(webEnvironment)
|
||||
aggregatorParentConfiguration.sources(sources).web(webEnvironment)
|
||||
.headless(headless)
|
||||
.properties("spring.jmx.default-domain="
|
||||
+ AggregateApplicationBuilder.ParentConfiguration.class.getName(),
|
||||
SELF_CONTAINED_APP_PROPERTY_NAME + "=" + selfContained);
|
||||
+ AggregateApplicationBuilder.ParentConfiguration.class.getName(),
|
||||
InternalPropertyNames.SELF_CONTAINED_APP_PROPERTY_NAME + "="
|
||||
+ selfContained);
|
||||
return aggregatorParentConfiguration.run(args);
|
||||
}
|
||||
|
||||
@@ -64,32 +56,31 @@ abstract class AggregateApplicationUtils {
|
||||
return appClassName + "_" + index;
|
||||
}
|
||||
|
||||
|
||||
protected static SpringApplicationBuilder embedApp(
|
||||
ConfigurableApplicationContext parentContext, String namespace,
|
||||
Class<?> app) {
|
||||
return new SpringApplicationBuilder(app)
|
||||
.web(false)
|
||||
.main(app)
|
||||
.bannerMode(Mode.OFF)
|
||||
return new SpringApplicationBuilder(app).web(false).main(app).bannerMode(Mode.OFF)
|
||||
.properties("spring.jmx.default-domain=" + namespace)
|
||||
.properties(CHANNEL_NAMESPACE_PROPERTY_NAME + "=" + namespace)
|
||||
.registerShutdownHook(false)
|
||||
.parent(parentContext);
|
||||
.properties(
|
||||
InternalPropertyNames.NAMESPACE_PROPERTY_NAME + "=" + namespace)
|
||||
.registerShutdownHook(false).parent(parentContext);
|
||||
}
|
||||
|
||||
static void prepareSharedChannelRegistry(SharedChannelRegistry sharedChannelRegistry,
|
||||
static void prepareSharedBindingTargetRegistry(
|
||||
SharedBindingTargetRegistry sharedBindingTargetRegistry,
|
||||
LinkedHashMap<Class<?>, String> appsWithNamespace) {
|
||||
int i = 0;
|
||||
SubscribableChannel sharedChannel = null;
|
||||
for (Entry<Class<?>, String> appEntry : appsWithNamespace.entrySet()) {
|
||||
String namespace = appEntry.getValue();
|
||||
if (i > 0) {
|
||||
sharedChannelRegistry.register(namespace + "." + INPUT_CHANNEL_NAME, sharedChannel);
|
||||
sharedBindingTargetRegistry.register(namespace + "." + INPUT_BINDING_NAME,
|
||||
sharedChannel);
|
||||
}
|
||||
sharedChannel = new DirectChannel();
|
||||
if (i < appsWithNamespace.size() - 1) {
|
||||
sharedChannelRegistry.register(namespace + "." + OUTPUT_CHANNEL_NAME, sharedChannel);
|
||||
sharedBindingTargetRegistry.register(namespace + "." + OUTPUT_BINDING_NAME,
|
||||
sharedChannel);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.aggregate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
/**
|
||||
* Stores binding targets shared by the components of an aggregate application.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public class SharedBindingTargetRegistry {
|
||||
|
||||
private Map<String, Object> sharedBindingTargets = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
public <T> T get(String id, Class<T> bindingTargetType) {
|
||||
Object sharedBindingTarget = this.sharedBindingTargets.get(id);
|
||||
if (sharedBindingTarget == null) {
|
||||
return null;
|
||||
}
|
||||
if (!bindingTargetType.isAssignableFrom(sharedBindingTarget.getClass())) {
|
||||
throw new IllegalArgumentException("A shared " + bindingTargetType.getName() + " was requested, "
|
||||
+ "but the existing shared target with id '" + id + "' is a " + sharedBindingTarget.getClass());
|
||||
}
|
||||
else {
|
||||
return (T) sharedBindingTarget;
|
||||
}
|
||||
}
|
||||
|
||||
public void register(String id, Object bindingTarget) {
|
||||
this.sharedBindingTargets.put(id, bindingTarget);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAll() {
|
||||
return Collections.unmodifiableMap(this.sharedBindingTargets);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,31 +17,45 @@
|
||||
package org.springframework.cloud.stream.aggregate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* A registry for channels that can be shared between modules, used for module aggregation.
|
||||
* Wraps the {@link SharedBindingTargetRegistry} for access to {@link MessageChannel}
|
||||
* instances. This class is provided as a convenience for users of
|
||||
* {@link SharedChannelRegistry} in previous versions and will be removed in the future.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @deprecated in favour of {@link SharedBindingTargetRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public class SharedChannelRegistry {
|
||||
|
||||
/**
|
||||
* A {@link Map} of channels, indexed by name. A channel's name may be prefixed by a namespace.
|
||||
*/
|
||||
private Map<String, MessageChannel> sharedChannels = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
private final SharedBindingTargetRegistry sharedBindingTargetRegistry;
|
||||
|
||||
public MessageChannel get(String id) {
|
||||
return sharedChannels.get(id);
|
||||
public SharedChannelRegistry(SharedBindingTargetRegistry sharedBindingTargetRegistry) {
|
||||
this.sharedBindingTargetRegistry = sharedBindingTargetRegistry;
|
||||
}
|
||||
|
||||
public void register(String id, MessageChannel messageChannel) {
|
||||
this.sharedChannels.put(id, messageChannel);
|
||||
public MessageChannel get(String id) {
|
||||
return this.sharedBindingTargetRegistry.get(id, MessageChannel.class);
|
||||
}
|
||||
|
||||
public void register(String id, MessageChannel bindingTarget) {
|
||||
this.sharedBindingTargetRegistry.register(id, bindingTarget);
|
||||
}
|
||||
|
||||
public Map<String, MessageChannel> getAll() {
|
||||
return Collections.unmodifiableMap(this.sharedChannels);
|
||||
Map<String, Object> sharedBindingTargets = this.sharedBindingTargetRegistry.getAll();
|
||||
Map<String, MessageChannel> sharedMessageChannels = new HashMap<>();
|
||||
for (Map.Entry<String, Object> sharedBindingTargetEntry : sharedBindingTargets.entrySet()) {
|
||||
if (MessageChannel.class.isAssignableFrom(sharedBindingTargetEntry.getValue().getClass())) {
|
||||
sharedMessageChannels.put(sharedBindingTargetEntry.getKey(),
|
||||
(MessageChannel) sharedBindingTargetEntry.getValue());
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(sharedMessageChannels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,33 +25,33 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.stream.config.BinderFactoryConfiguration;
|
||||
import org.springframework.cloud.stream.config.BindingBeansRegistrar;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceConfiguration;
|
||||
import org.springframework.cloud.stream.config.BindingServiceConfiguration;
|
||||
import org.springframework.cloud.stream.config.SpelExpressionConverterConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
|
||||
/**
|
||||
* Enables the binding of components annotated with {@link Input} and {@link Output} to a broker, according to the list
|
||||
* of interfaces passed as value to the annotation.
|
||||
* Enables the binding of targets annotated with {@link Input} and {@link Output} to a
|
||||
* broker, according to the list of interfaces passed as value to the annotation.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
* @author David Turanski
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Configuration
|
||||
@Import({ChannelBindingServiceConfiguration.class, BindingBeansRegistrar.class, BinderFactoryConfiguration.class,
|
||||
SpelExpressionConverterConfiguration.class})
|
||||
@Import({ BindingServiceConfiguration.class, BindingBeansRegistrar.class, BinderFactoryConfiguration.class,
|
||||
SpelExpressionConverterConfiguration.class })
|
||||
@EnableIntegration
|
||||
public @interface EnableBinding {
|
||||
|
||||
/**
|
||||
* A list of interfaces having methods annotated with {@link Input} and/or
|
||||
* {@link Output} to indicate bindable components.
|
||||
* {@link Output} to indicate binding targets.
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Indicates that an input channel will be created by the framework.
|
||||
* Indicates that an input binding target will be created by the framework.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Indicates that an output channel will be created by the framework.
|
||||
* Indicates that an output binding target will be created by the framework.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
|
||||
@@ -36,34 +36,35 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
* <h3>Declarative mode</h3>
|
||||
*
|
||||
* A method is considered declarative if all its method parameter types and return type
|
||||
* (if not void) are bound elements or conversion targets from bound elements via a
|
||||
* (if not void) are binding targets or conversion targets from binding targets via a
|
||||
* registered {@link StreamListenerParameterAdapter}.
|
||||
*
|
||||
* Only declarative methods can have bound elements or conversion targets as arguments and
|
||||
* return type.
|
||||
* Only declarative methods can have binding targets or conversion targets as arguments
|
||||
* and return type.
|
||||
*
|
||||
* Declarative methods must specify what inputs and outputs correspond to their arguments
|
||||
* and return type, and can do this in one of the following ways.
|
||||
*
|
||||
* <ul>
|
||||
* <li>By using either the {@link Input} or {@link Output} annotation for each of the
|
||||
* parameters and the {@link Output} annotation on the method for the return type (if applicable). The use
|
||||
* of annotations in this case is mandatory. In this case the {@link StreamListener}
|
||||
* annotation must not specify a value.</li>
|
||||
* <li>By setting an {@link Input} bound target as the annotation value of {@link StreamListener}
|
||||
* and using {@link org.springframework.messaging.handler.annotation.SendTo}</li> on the method
|
||||
* for the return type (if applicable). In this case the method must have exactly one parameter, corresponding
|
||||
* to an input.</li>
|
||||
* parameters and the {@link Output} annotation on the method for the return type (if
|
||||
* applicable). The use of annotations in this case is mandatory. In this case the
|
||||
* {@link StreamListener} annotation must not specify a value.</li>
|
||||
* <li>By setting an {@link Input} bound target as the annotation value of
|
||||
* {@link StreamListener} and using
|
||||
* {@link org.springframework.messaging.handler.annotation.SendTo}</li> on the method for
|
||||
* the return type (if applicable). In this case the method must have exactly one
|
||||
* parameter, corresponding to an input.</li>
|
||||
* </ul>
|
||||
*
|
||||
* An example of declarative method signature using the former idiom is as follows:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* @StreamListener
|
||||
* @StreamListener
|
||||
* public @Output("joined") Flux<String> join(
|
||||
* @Input("input1") Flux<String> input1,
|
||||
* @Input("input2") Flux<String> input2) {
|
||||
* @Input("input1") Flux<String> input1,
|
||||
* @Input("input2") Flux<String> input2) {
|
||||
* // ... join the two input streams via functional operators
|
||||
* }
|
||||
* }
|
||||
@@ -73,8 +74,8 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* @StreamListener(Processor.INPUT)
|
||||
* @SendTo(Processor.OUTPUT)
|
||||
* @StreamListener(Processor.INPUT)
|
||||
* @SendTo(Processor.OUTPUT)
|
||||
* public Flux<String> convert(Flux<String> input) {
|
||||
* return input.map(String::toUppercase);
|
||||
* }
|
||||
@@ -90,7 +91,7 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
* flexible signature, as described by {@link MessageMapping}.
|
||||
*
|
||||
* If the method returns a {@link org.springframework.messaging.Message}, the result will
|
||||
* be automatically sent to a channel, as follows:
|
||||
* be automatically sent to a binding target, as follows:
|
||||
* <ul>
|
||||
* <li>A result of the type {@link org.springframework.messaging.Message} will be sent
|
||||
* as-is</li>
|
||||
@@ -98,8 +99,8 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
* {@link org.springframework.messaging.Message}</li>
|
||||
* </ul>
|
||||
*
|
||||
* The target channel of the return message is determined by consulting in the following
|
||||
* order:
|
||||
* The output binding target where the return message is sent is determined by consulting
|
||||
* in the following order:
|
||||
* <ul>
|
||||
* <li>The {@link org.springframework.messaging.MessageHeaders} of the resulting
|
||||
* message.</li>
|
||||
@@ -112,12 +113,8 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* @StreamListener(Processor.INPUT)
|
||||
* @SendTo(Processor.OUTPUT)
|
||||
* public String convert(String input) {
|
||||
* return input.toUppercase();
|
||||
* }
|
||||
* }
|
||||
* @StreamListener(Processor.INPUT) @SendTo(Processor.OUTPUT) public String convert(String
|
||||
* input) { return input.toUppercase(); } }
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
@@ -132,7 +129,7 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
public @interface StreamListener {
|
||||
|
||||
/**
|
||||
* The name of the bound component (e.g. channel) that the method subscribes to.
|
||||
* The name of the binding target (e.g. channel) that the method subscribes to.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for {@link Binder} implementations.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
* @author Ilayaperumal Gopinathan
|
||||
@@ -59,7 +61,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
implements ApplicationContextAware, InitializingBean, Binder<T, C, P> {
|
||||
|
||||
/**
|
||||
* The delimiter between a group and index when constructing a binder consumer/producer.
|
||||
* The delimiter between a group and index when constructing a binder
|
||||
* consumer/producer.
|
||||
*/
|
||||
private static final String GROUP_INDEX_DELIMITER = ".";
|
||||
|
||||
@@ -78,15 +81,15 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
/**
|
||||
* For binder implementations that support a prefix, apply the prefix to the name.
|
||||
* @param prefix the prefix.
|
||||
* @param name the name.
|
||||
* @param name the name.
|
||||
*/
|
||||
public static String applyPrefix(String prefix, String name) {
|
||||
return prefix + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* For binder implementations that support dead lettering, construct the name of the dead letter entity for the
|
||||
* underlying pipe name.
|
||||
* For binder implementations that support dead lettering, construct the name of the
|
||||
* dead letter entity for the underlying pipe name.
|
||||
* @param name the name.
|
||||
*/
|
||||
public static String constructDLQName(String name) {
|
||||
@@ -125,8 +128,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may implement this method to perform any necessary initialization.
|
||||
* It will be invoked from {@link #afterPropertiesSet()} which is itself {@code final}.
|
||||
* Subclasses may implement this method to perform any necessary initialization. It
|
||||
* will be invoked from {@link #afterPropertiesSet()} which is itself {@code final}.
|
||||
*/
|
||||
protected void onInit() throws Exception {
|
||||
// no-op default
|
||||
@@ -135,8 +138,7 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
@Override
|
||||
public final Binding<T> bindConsumer(String name, String group, T target, C properties) {
|
||||
if (StringUtils.isEmpty(group)) {
|
||||
Assert.isTrue(!properties.isPartitioned(),
|
||||
"A consumer group is required for a partitioned subscription");
|
||||
Assert.isTrue(!properties.isPartitioned(), "A consumer group is required for a partitioned subscription");
|
||||
}
|
||||
return doBindConsumer(name, group, target, properties);
|
||||
}
|
||||
@@ -152,7 +154,7 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
|
||||
/**
|
||||
* Construct a name comprised of the name and group.
|
||||
* @param name the name.
|
||||
* @param name the name.
|
||||
* @param group the group.
|
||||
* @return the constructed name.
|
||||
*/
|
||||
@@ -164,7 +166,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
Object originalPayload = message.getPayload();
|
||||
Object originalContentType = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
|
||||
|
||||
//Pass content type as String since some transport adapters will exclude CONTENT_TYPE Header otherwise
|
||||
// Pass content type as String since some transport adapters will exclude
|
||||
// CONTENT_TYPE Header otherwise
|
||||
Object contentType = JavaClassMimeTypeConversion
|
||||
.mimeTypeFromObject(originalPayload, ObjectUtils.nullSafeToString(originalContentType)).toString();
|
||||
Object payload = serializePayloadIfNecessary(originalPayload);
|
||||
@@ -191,8 +194,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
return bos.toByteArray();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new SerializationFailedException("unable to serialize payload ["
|
||||
+ originalPayload.getClass().getName() + "]", e);
|
||||
throw new SerializationFailedException(
|
||||
"unable to serialize payload [" + originalPayload.getClass().getName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,7 +211,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
if (payload != null) {
|
||||
messageValues.setPayload(payload);
|
||||
Object originalContentType = messageValues.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE);
|
||||
// Reset content-type only if the original content type is not null (when receiving messages from
|
||||
// Reset content-type only if the original content type is not null (when
|
||||
// receiving messages from
|
||||
// non-SCSt applications).
|
||||
if (originalContentType != null) {
|
||||
messageValues.put(MessageHeaders.CONTENT_TYPE, originalContentType);
|
||||
@@ -236,8 +240,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
return new String(bytes, "UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new SerializationFailedException("unable to deserialize [java.lang.String]. Encoding not supported.",
|
||||
e);
|
||||
throw new SerializationFailedException(
|
||||
"unable to deserialize [java.lang.String]. Encoding not supported.", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -253,7 +257,7 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new SerializationFailedException("unable to deserialize [" + className + "]. Class not found.",
|
||||
e); //NOSONAR
|
||||
e); // NOSONAR
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new SerializationFailedException("unable to deserialize [" + className + "]", e);
|
||||
@@ -307,7 +311,8 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
if (mimeType == null) {
|
||||
String modifiedClassName = className;
|
||||
if (payload.getClass().isArray()) {
|
||||
// Need to remove trailing ';' for an object array, e.g. "[Ljava.lang.String;" or multi-dimensional
|
||||
// Need to remove trailing ';' for an object array, e.g.
|
||||
// "[Ljava.lang.String;" or multi-dimensional
|
||||
// "[[[Ljava.lang.String;"
|
||||
if (modifiedClassName.endsWith(";")) {
|
||||
modifiedClassName = modifiedClassName.substring(0, modifiedClassName.length() - 1);
|
||||
@@ -327,7 +332,7 @@ public abstract class AbstractBinder<T, C extends ConsumerProperties, P extends
|
||||
if (className == null) {
|
||||
return null;
|
||||
}
|
||||
//unwrap quotes if any
|
||||
// unwrap quotes if any
|
||||
className = className.replace("\"", "");
|
||||
|
||||
// restore trailing ';'
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
package org.springframework.cloud.stream.binder;
|
||||
|
||||
/**
|
||||
* A strategy interface used to bind an app interface to a logical name. The name is intended to identify a
|
||||
* logical consumer or producer of messages. This may be a queue, a channel adapter, another message channel, a Spring
|
||||
* bean, etc.
|
||||
* A strategy interface used to bind an app interface to a logical name. The name is
|
||||
* intended to identify a logical consumer or producer of messages. This may be a queue, a
|
||||
* channel adapter, another message channel, a Spring bean, etc.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author David Turanski
|
||||
@@ -32,18 +32,21 @@ package org.springframework.cloud.stream.binder;
|
||||
public interface Binder<T, C extends ConsumerProperties, P extends ProducerProperties> {
|
||||
|
||||
/**
|
||||
* Bind the target component as a message consumer to the logical entity identified by the name.
|
||||
* Bind the target component as a message consumer to the logical entity identified by
|
||||
* the name.
|
||||
* @param name the logical identity of the message source
|
||||
* @param group the consumer group to which this consumer belongs - subscriptions are shared among consumers
|
||||
* in the same group (a <code>null</code> or empty String, must be treated as an anonymous group that doesn't share
|
||||
* the subscription with any other consumer)
|
||||
* @param group the consumer group to which this consumer belongs - subscriptions are
|
||||
* shared among consumers in the same group (a <code>null</code> or empty String, must
|
||||
* be treated as an anonymous group that doesn't share the subscription with any other
|
||||
* consumer)
|
||||
* @param inboundBindTarget the app interface to be bound as a consumer
|
||||
* @param consumerProperties the consumer properties
|
||||
*/
|
||||
Binding<T> bindConsumer(String name, String group, T inboundBindTarget, C consumerProperties);
|
||||
|
||||
/**
|
||||
* Bind the target component as a message producer to the logical entity identified by the name.
|
||||
* Bind the target component as a message producer to the logical entity identified by
|
||||
* the name.
|
||||
* @param name the logical identity of the message target
|
||||
* @param outboundBindTarget the app interface to be bound as a producer
|
||||
* @param producerProperties the producer properties
|
||||
|
||||
@@ -19,8 +19,9 @@ package org.springframework.cloud.stream.binder;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Configuration for a binder instance, associating a {@link BinderType} with its configuration {@link Properties}.
|
||||
* An application may contain multiple {@link BinderConfiguration}s per {@link BinderType}, when connecting to multiple
|
||||
* Configuration for a binder instance, associating a {@link BinderType} with its
|
||||
* configuration {@link Properties}. An application may contain multiple
|
||||
* {@link BinderConfiguration}s per {@link BinderType}, when connecting to multiple
|
||||
* systems of the same type.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
@@ -38,8 +39,10 @@ public class BinderConfiguration {
|
||||
/**
|
||||
* @param binderType the binder type used by this configuration
|
||||
* @param properties the properties for setting up the binder
|
||||
* @param inheritEnvironment whether the binder should inherit the environment of the application
|
||||
* @param defaultCandidate whether the binder should be considered as a candidate when determining a default
|
||||
* @param inheritEnvironment whether the binder should inherit the environment of the
|
||||
* application
|
||||
* @param defaultCandidate whether the binder should be considered as a candidate when
|
||||
* determining a default
|
||||
*/
|
||||
public BinderConfiguration(BinderType binderType, Properties properties, boolean inheritEnvironment,
|
||||
boolean defaultCandidate) {
|
||||
|
||||
@@ -19,14 +19,16 @@ package org.springframework.cloud.stream.binder;
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface BinderFactory<T> {
|
||||
public interface BinderFactory {
|
||||
|
||||
/**
|
||||
* Returns the binder instance associated with the given configuration name. Instance caching is a requirement,
|
||||
* and implementations must return the same instance on subsequent invocations with the same argument.
|
||||
* Returns the binder instance associated with the given configuration name. Instance
|
||||
* caching is a requirement, and implementations must return the same instance on
|
||||
* subsequent invocations with the same arguments.
|
||||
*
|
||||
* @param configurationName the name of a binder configuration
|
||||
* @return the binder instance
|
||||
*/
|
||||
Binder<T, ?, ?> getBinder(String configurationName);
|
||||
<T> Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties> getBinder(String configurationName,
|
||||
Class<? extends T> bindableType);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.stream.reflection.GenericsUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -48,14 +49,16 @@ import org.springframework.util.StringUtils;
|
||||
* Default {@link BinderFactory} implementation.
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class DefaultBinderFactory<T> implements BinderFactory<T>, DisposableBean, ApplicationContextAware {
|
||||
public class DefaultBinderFactory implements BinderFactory, DisposableBean, ApplicationContextAware {
|
||||
|
||||
private final Map<String, BinderConfiguration> binderConfigurations;
|
||||
|
||||
private final Map<String, BinderInstanceHolder<T>> binderInstanceCache = new HashMap<>();
|
||||
private final Map<String, BinderInstanceHolder> binderInstanceCache = new HashMap<>();
|
||||
|
||||
private volatile ConfigurableApplicationContext context;
|
||||
|
||||
private Map<String, String> defaultBinderForBindingTargetType = new HashMap<>();
|
||||
|
||||
private volatile String defaultBinder;
|
||||
|
||||
private volatile CompositeHealthIndicator bindersHealthIndicator;
|
||||
@@ -82,14 +85,15 @@ public class DefaultBinderFactory<T> implements BinderFactory<T>, DisposableBean
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
for (Map.Entry<String, BinderInstanceHolder<T>> entry : this.binderInstanceCache.entrySet()) {
|
||||
BinderInstanceHolder<T> binderInstanceHolder = entry.getValue();
|
||||
for (Map.Entry<String, BinderInstanceHolder> entry : this.binderInstanceCache.entrySet()) {
|
||||
BinderInstanceHolder binderInstanceHolder = entry.getValue();
|
||||
binderInstanceHolder.getBinderContext().close();
|
||||
}
|
||||
this.defaultBinderForBindingTargetType.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Binder<T, ?, ?> getBinder(String name) {
|
||||
public synchronized <T> Binder<T, ?, ?> getBinder(String name, Class<? extends T> bindingTargetType) {
|
||||
String configurationName;
|
||||
// Fall back to a default if no argument is provided
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
@@ -106,37 +110,57 @@ public class DefaultBinderFactory<T> implements BinderFactory<T>, DisposableBean
|
||||
}
|
||||
}
|
||||
if (defaultCandidateConfigurations.size() == 1) {
|
||||
this.defaultBinder = defaultCandidateConfigurations.iterator().next();
|
||||
configurationName = this.defaultBinder;
|
||||
configurationName = defaultCandidateConfigurations.iterator().next();
|
||||
this.defaultBinderForBindingTargetType.put(bindingTargetType.getName(), configurationName);
|
||||
}
|
||||
else {
|
||||
if (defaultCandidateConfigurations.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"A default binder has been requested, but there is more than one binder available: "
|
||||
+ StringUtils.collectionToCommaDelimitedString(defaultCandidateConfigurations)
|
||||
+ ", and no default binder has been set.");
|
||||
List<String> candidatesForBindableType = new ArrayList<>();
|
||||
for (String defaultCandidateConfiguration : defaultCandidateConfigurations) {
|
||||
Binder<Object, ?, ?> binderInstance = getBinderInstance(defaultCandidateConfiguration);
|
||||
Class<?> binderType = GenericsUtils.getParameterType(binderInstance.getClass(), Binder.class, 0);
|
||||
if (binderType.isAssignableFrom(bindingTargetType)) {
|
||||
candidatesForBindableType.add(defaultCandidateConfiguration);
|
||||
}
|
||||
}
|
||||
if (candidatesForBindableType.size() == 1) {
|
||||
configurationName = candidatesForBindableType.iterator().next();
|
||||
this.defaultBinderForBindingTargetType.put(bindingTargetType.getName(), configurationName);
|
||||
} else if (candidatesForBindableType.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"A default binder has been requested, but there is more than one binder available for '"
|
||||
+ bindingTargetType.getName() + "' : "
|
||||
+ StringUtils.collectionToCommaDelimitedString(candidatesForBindableType)
|
||||
+ ", and no default binder has been set.");
|
||||
} else {
|
||||
throw new IllegalStateException("A default binder has been requested, but none of the " +
|
||||
"registered binders can bind a '" + bindingTargetType + "': "
|
||||
+ StringUtils.collectionToCommaDelimitedString(defaultCandidateConfigurations));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"A default binder has been requested, but there there is no binder available");
|
||||
throw new IllegalArgumentException(
|
||||
"A default binder has been requested, but there is no default available");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (StringUtils.hasText(this.defaultBinder)) {
|
||||
configurationName = this.defaultBinder;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"A default binder has been requested, but there is more than one binder available: "
|
||||
+ StringUtils.collectionToCommaDelimitedString(this.binderConfigurations.keySet())
|
||||
+ ", and no default binder has been set.");
|
||||
}
|
||||
configurationName = this.defaultBinder;
|
||||
}
|
||||
}
|
||||
else {
|
||||
configurationName = name;
|
||||
}
|
||||
Binder<T, ?, ?> binderInstance = getBinderInstance(configurationName);
|
||||
if (!(GenericsUtils.getParameterType(binderInstance.getClass(), Binder.class, 0)
|
||||
.isAssignableFrom(bindingTargetType))) {
|
||||
throw new IllegalStateException(
|
||||
"The binder '" + configurationName + "' cannot bind a " + bindingTargetType.getName());
|
||||
}
|
||||
return binderInstance;
|
||||
}
|
||||
|
||||
private <T> Binder<T, ?, ?> getBinderInstance(String configurationName) {
|
||||
if (!this.binderInstanceCache.containsKey(configurationName)) {
|
||||
BinderConfiguration binderConfiguration = this.binderConfigurations.get(configurationName);
|
||||
if (binderConfiguration == null) {
|
||||
@@ -196,28 +220,27 @@ public class DefaultBinderFactory<T> implements BinderFactory<T>, DisposableBean
|
||||
healthAggregator, indicators);
|
||||
this.bindersHealthIndicator.addHealthIndicator(configurationName, binderHealthIndicator);
|
||||
}
|
||||
this.binderInstanceCache.put(configurationName, new BinderInstanceHolder<>(binder,
|
||||
this.binderInstanceCache.put(configurationName, new BinderInstanceHolder(binder,
|
||||
binderProducingContext));
|
||||
}
|
||||
return this.binderInstanceCache.get(configurationName).getBinderInstance();
|
||||
return (Binder<T, ?, ?>) this.binderInstanceCache.get(configurationName).getBinderInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for storing {@link Binder} instances, along with their associated contexts.
|
||||
* @param <T>
|
||||
*/
|
||||
private static final class BinderInstanceHolder<T> {
|
||||
private static final class BinderInstanceHolder {
|
||||
|
||||
private final Binder<T, ?, ?> binderInstance;
|
||||
private final Binder<?, ?, ?> binderInstance;
|
||||
|
||||
private final ConfigurableApplicationContext binderContext;
|
||||
|
||||
private BinderInstanceHolder(Binder<T, ?, ?> binderInstance, ConfigurableApplicationContext binderContext) {
|
||||
private BinderInstanceHolder(Binder<?, ?, ?> binderInstance, ConfigurableApplicationContext binderContext) {
|
||||
this.binderInstance = binderInstance;
|
||||
this.binderContext = binderContext;
|
||||
}
|
||||
|
||||
public Binder<T, ?, ?> getBinderInstance() {
|
||||
public Binder<?, ?, ?> getBinderInstance() {
|
||||
return this.binderInstance;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.binding;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link BindingTargetFactory} implementation that restricts the type of binding target
|
||||
* to a specified class and its supertypes.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public abstract class AbstractBindingTargetFactory<T> implements BindingTargetFactory {
|
||||
|
||||
private final Class<T> bindingTargetType;
|
||||
|
||||
protected AbstractBindingTargetFactory(Class<T> bindingTargetType) {
|
||||
Assert.notNull(bindingTargetType, "The binding target type cannot be null");
|
||||
this.bindingTargetType = bindingTargetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean canCreate(Class<?> clazz) {
|
||||
return clazz.isAssignableFrom(this.bindingTargetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract T createInput(String name);
|
||||
|
||||
@Override
|
||||
public abstract T createOutput(String name);
|
||||
}
|
||||
@@ -30,22 +30,22 @@ public interface Bindable {
|
||||
/**
|
||||
* Binds all the inputs associated with this instance.
|
||||
*/
|
||||
void bindInputs(ChannelBindingService adapter);
|
||||
void bindInputs(BindingService adapter);
|
||||
|
||||
/**
|
||||
* Binds all the outputs associated with this instance.
|
||||
*/
|
||||
void bindOutputs(ChannelBindingService adapter);
|
||||
void bindOutputs(BindingService adapter);
|
||||
|
||||
/**
|
||||
* Unbinds all the inputs associated with this instance.
|
||||
*/
|
||||
void unbindInputs(ChannelBindingService adapter);
|
||||
void unbindInputs(BindingService adapter);
|
||||
|
||||
/**
|
||||
* Unbinds all the outputs associated with this instance.
|
||||
*/
|
||||
void unbindOutputs(ChannelBindingService adapter);
|
||||
void unbindOutputs(BindingService adapter);
|
||||
|
||||
/**
|
||||
* Enumerates all the input binding names.
|
||||
|
||||
@@ -28,19 +28,19 @@ public class BindableAdapter implements Bindable {
|
||||
|
||||
|
||||
@Override
|
||||
public void bindInputs(ChannelBindingService adapter) {
|
||||
public void bindInputs(BindingService adapter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindOutputs(ChannelBindingService adapter) {
|
||||
public void bindOutputs(BindingService adapter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindInputs(ChannelBindingService adapter) {
|
||||
public void unbindInputs(BindingService adapter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindOutputs(ChannelBindingService adapter) {
|
||||
public void unbindOutputs(BindingService adapter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,44 +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.binding;
|
||||
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
* Defines methods to create/configure the {@link org.springframework.messaging.MessageChannel}s defined
|
||||
* in {@link org.springframework.cloud.stream.annotation.EnableBinding}.
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public interface BindableChannelFactory {
|
||||
|
||||
/**
|
||||
* Create an input {@link SubscribableChannel} that will be bound via
|
||||
* the message channel {@link org.springframework.cloud.stream.binder.Binder}.
|
||||
* @param name name of the message channel
|
||||
* @return subscribable message channel
|
||||
*/
|
||||
SubscribableChannel createInputChannel(String name);
|
||||
|
||||
/**
|
||||
* Create an output {@link SubscribableChannel} that will be bound via
|
||||
* the message channel {@link org.springframework.cloud.stream.binder.Binder}.
|
||||
* @param name name of the message channel
|
||||
* @return subscribable message channel
|
||||
*/
|
||||
SubscribableChannel createOutputChannel(String name);
|
||||
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
package org.springframework.cloud.stream.binding;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -31,15 +33,15 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.stream.aggregate.SharedChannelRegistry;
|
||||
import org.springframework.cloud.stream.aggregate.SharedBindingTargetRegistry;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.cloud.stream.internal.InternalPropertyNames;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for instantiating the interfaces specified via
|
||||
@@ -55,26 +57,22 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
|
||||
|
||||
private static Log log = LogFactory.getLog(BindableProxyFactory.class);
|
||||
|
||||
private static final String SPRING_CLOUD_STREAM_INTERNAL_PREFIX = "spring.cloud.stream.internal";
|
||||
|
||||
private static final String CHANNEL_NAMESPACE_PROPERTY_NAME = SPRING_CLOUD_STREAM_INTERNAL_PREFIX + ".channelNamespace";
|
||||
|
||||
@Value("${" + CHANNEL_NAMESPACE_PROPERTY_NAME + ":}")
|
||||
private String channelNamespace;
|
||||
|
||||
@Autowired
|
||||
private BindableChannelFactory channelFactory;
|
||||
@Value("${" + InternalPropertyNames.NAMESPACE_PROPERTY_NAME + ":}")
|
||||
private String namespace;
|
||||
|
||||
@Autowired(required = false)
|
||||
private SharedChannelRegistry sharedChannelRegistry;
|
||||
private SharedBindingTargetRegistry sharedBindingTargetRegistry;
|
||||
|
||||
@Autowired
|
||||
private Map<String, BindingTargetFactory> bindingTargetFactories;
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
private Object proxy;
|
||||
|
||||
private Map<String, ChannelHolder> inputHolders = new HashMap<>();
|
||||
private Map<String, BoundTargetHolder> inputHolders = new HashMap<>();
|
||||
|
||||
private Map<String, ChannelHolder> outputHolders = new HashMap<>();
|
||||
private Map<String, BoundTargetHolder> outputHolders = new HashMap<>();
|
||||
|
||||
public BindableProxyFactory(Class<?> type) {
|
||||
this.type = type;
|
||||
@@ -82,41 +80,40 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
|
||||
|
||||
@Override
|
||||
public synchronized Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
MessageChannel messageChannel = null;
|
||||
Method method = invocation.getMethod();
|
||||
if (MessageChannel.class.isAssignableFrom(method.getReturnType())) {
|
||||
Input input = AnnotationUtils.findAnnotation(method, Input.class);
|
||||
if (input != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils.getChannelName(input, method);
|
||||
messageChannel = this.inputHolders.get(name).getMessageChannel();
|
||||
}
|
||||
Input input = AnnotationUtils.findAnnotation(method, Input.class);
|
||||
if (input != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(input, method);
|
||||
return this.inputHolders.get(name).getBoundTarget();
|
||||
}
|
||||
else {
|
||||
Output output = AnnotationUtils.findAnnotation(method, Output.class);
|
||||
if (output != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils.getChannelName(output, method);
|
||||
messageChannel = this.outputHolders.get(name).getMessageChannel();
|
||||
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(output, method);
|
||||
return this.outputHolders.get(name).getBoundTarget();
|
||||
}
|
||||
}
|
||||
//ignore
|
||||
return messageChannel;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(BindableProxyFactory.this.channelFactory, "Channel Factory cannot be null");
|
||||
Assert.notEmpty(BindableProxyFactory.this.bindingTargetFactories, "'bindingTargetFactories' cannot be empty");
|
||||
ReflectionUtils.doWithMethods(this.type, new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException {
|
||||
Input input = AnnotationUtils.findAnnotation(method, Input.class);
|
||||
if (input != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils.getChannelName(input, method);
|
||||
validateChannelType(method.getReturnType());
|
||||
MessageChannel sharedChannel = locateSharedChannel(name);
|
||||
if (sharedChannel == null) {
|
||||
BindableProxyFactory.this.inputHolders.put(name, new ChannelHolder(
|
||||
BindableProxyFactory.this.channelFactory.createInputChannel(name), true));
|
||||
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(input, method);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
Object sharedBindingTarget = locateSharedBindingTarget(name, returnType);
|
||||
if (sharedBindingTarget != null) {
|
||||
BindableProxyFactory.this.inputHolders.put(name,
|
||||
new BoundTargetHolder(sharedBindingTarget, false));
|
||||
}
|
||||
else {
|
||||
BindableProxyFactory.this.inputHolders.put(name, new ChannelHolder(sharedChannel, false));
|
||||
BindableProxyFactory.this.inputHolders.put(name,
|
||||
new BoundTargetHolder(getBindingTargetFactory(returnType).createInput(name), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,35 +123,55 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
|
||||
public void doWith(Method method) throws IllegalArgumentException {
|
||||
Output output = AnnotationUtils.findAnnotation(method, Output.class);
|
||||
if (output != null) {
|
||||
String name = BindingBeanDefinitionRegistryUtils.getChannelName(output, method);
|
||||
validateChannelType(method.getReturnType());
|
||||
MessageChannel sharedChannel = locateSharedChannel(name);
|
||||
if (sharedChannel == null) {
|
||||
BindableProxyFactory.this.outputHolders.put(name, new ChannelHolder(
|
||||
BindableProxyFactory.this.channelFactory.createOutputChannel(name), true));
|
||||
String name = BindingBeanDefinitionRegistryUtils.getBindingTargetName(output, method);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
Object sharedBindingTarget = locateSharedBindingTarget(name, returnType);
|
||||
if (sharedBindingTarget != null) {
|
||||
BindableProxyFactory.this.outputHolders.put(name,
|
||||
new BoundTargetHolder(sharedBindingTarget, false));
|
||||
}
|
||||
else {
|
||||
BindableProxyFactory.this.outputHolders.put(name, new ChannelHolder(sharedChannel, false));
|
||||
BindableProxyFactory.this.outputHolders.put(name,
|
||||
new BoundTargetHolder(getBindingTargetFactory(returnType).createOutput(name), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void validateChannelType(Class<?> channelType) {
|
||||
Assert.isTrue(SubscribableChannel.class.equals(channelType) || MessageChannel.class.equals(channelType),
|
||||
"A bound channel should be either a '" + MessageChannel.class.getName() + "', " +
|
||||
" or a '" + SubscribableChannel.class.getName() + "'");
|
||||
private BindingTargetFactory getBindingTargetFactory(Class<?> bindingTargetType) {
|
||||
List<String> candidateBindingTargetFactories = new ArrayList<>();
|
||||
for (Map.Entry<String, BindingTargetFactory> bindingTargetFactoryEntry : this.bindingTargetFactories
|
||||
.entrySet()) {
|
||||
if (bindingTargetFactoryEntry.getValue().canCreate(bindingTargetType)) {
|
||||
candidateBindingTargetFactories.add(bindingTargetFactoryEntry.getKey());
|
||||
}
|
||||
}
|
||||
if (candidateBindingTargetFactories.size() == 1) {
|
||||
return this.bindingTargetFactories.get(candidateBindingTargetFactories.get(0));
|
||||
}
|
||||
else {
|
||||
if (candidateBindingTargetFactories.size() == 0) {
|
||||
throw new IllegalStateException("No factory found for binding target type: "
|
||||
+ bindingTargetType.getName() + " among registered factories: "
|
||||
+ StringUtils.collectionToCommaDelimitedString(bindingTargetFactories.keySet()));
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Multiple factories found for binding target type: " + bindingTargetType.getName() + ": "
|
||||
+ StringUtils.collectionToCommaDelimitedString(candidateBindingTargetFactories));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MessageChannel locateSharedChannel(String name) {
|
||||
return this.sharedChannelRegistry != null ?
|
||||
this.sharedChannelRegistry.get(getNamespacePrefixedChannelName(name)) : null;
|
||||
private <T> T locateSharedBindingTarget(String name, Class<T> bindingTargetType) {
|
||||
return this.sharedBindingTargetRegistry != null
|
||||
? this.sharedBindingTargetRegistry.get(getNamespacePrefixedBindingTargetName(name), bindingTargetType)
|
||||
: null;
|
||||
}
|
||||
|
||||
private String getNamespacePrefixedChannelName(String name) {
|
||||
return this.channelNamespace + "." + name;
|
||||
private String getNamespacePrefixedBindingTargetName(String name) {
|
||||
return this.namespace + "." + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,65 +194,67 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindInputs(ChannelBindingService channelBindingService) {
|
||||
public void bindInputs(BindingService bindingService) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Binding inputs for %s:%s", this.channelNamespace, this.type));
|
||||
log.debug(String.format("Binding inputs for %s:%s", this.namespace, this.type));
|
||||
}
|
||||
for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.inputHolders.entrySet()) {
|
||||
String inputChannelName = channelHolderEntry.getKey();
|
||||
ChannelHolder channelHolder = channelHolderEntry.getValue();
|
||||
if (channelHolder.isBindable()) {
|
||||
for (Map.Entry<String, BoundTargetHolder> boundTargetHolderEntry : this.inputHolders.entrySet()) {
|
||||
String inputTargetName = boundTargetHolderEntry.getKey();
|
||||
BoundTargetHolder boundTargetHolder = boundTargetHolderEntry.getValue();
|
||||
if (boundTargetHolder.isBindable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, inputChannelName));
|
||||
log.debug(String.format("Binding %s:%s:%s", this.namespace, this.type, inputTargetName));
|
||||
}
|
||||
channelBindingService.bindConsumer(channelHolder.getMessageChannel(), inputChannelName);
|
||||
bindingService.bindConsumer(boundTargetHolder.getBoundTarget(), inputTargetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindOutputs(ChannelBindingService channelBindingService) {
|
||||
public void bindOutputs(BindingService bindingService) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Binding outputs for %s:%s", this.channelNamespace, this.type));
|
||||
log.debug(String.format("Binding outputs for %s:%s", this.namespace, this.type));
|
||||
}
|
||||
for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.outputHolders.entrySet()) {
|
||||
ChannelHolder channelHolder = channelHolderEntry.getValue();
|
||||
String outputChannelName = channelHolderEntry.getKey();
|
||||
if (channelHolderEntry.getValue().isBindable()) {
|
||||
for (Map.Entry<String, BoundTargetHolder> boundTargetHolderEntry : this.outputHolders.entrySet()) {
|
||||
BoundTargetHolder boundTargetHolder = boundTargetHolderEntry.getValue();
|
||||
String outputTargetName = boundTargetHolderEntry.getKey();
|
||||
if (boundTargetHolderEntry.getValue().isBindable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, outputChannelName));
|
||||
log.debug(String.format("Binding %s:%s:%s", this.namespace, this.type, outputTargetName));
|
||||
}
|
||||
channelBindingService.bindProducer(channelHolder.getMessageChannel(), outputChannelName);
|
||||
bindingService.bindProducer(boundTargetHolder.getBoundTarget(), outputTargetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindInputs(ChannelBindingService channelBindingService) {
|
||||
public void unbindInputs(BindingService bindingService) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Unbinding inputs for %s:%s", this.channelNamespace, this.type));
|
||||
log.debug(String.format("Unbinding inputs for %s:%s", this.namespace, this.type));
|
||||
}
|
||||
for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.inputHolders.entrySet()) {
|
||||
if (channelHolderEntry.getValue().isBindable()) {
|
||||
for (Map.Entry<String, BoundTargetHolder> boundTargetHolderEntry : this.inputHolders.entrySet()) {
|
||||
if (boundTargetHolderEntry.getValue().isBindable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Unbinding %s:%s:%s", this.channelNamespace, this.type, channelHolderEntry.getKey()));
|
||||
log.debug(String.format("Unbinding %s:%s:%s", this.namespace, this.type,
|
||||
boundTargetHolderEntry.getKey()));
|
||||
}
|
||||
channelBindingService.unbindConsumers(channelHolderEntry.getKey());
|
||||
bindingService.unbindConsumers(boundTargetHolderEntry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindOutputs(ChannelBindingService channelBindingService) {
|
||||
public void unbindOutputs(BindingService bindingService) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Unbinding outputs for %s:%s", this.channelNamespace, this.type));
|
||||
log.debug(String.format("Unbinding outputs for %s:%s", this.namespace, this.type));
|
||||
}
|
||||
for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.outputHolders.entrySet()) {
|
||||
if (channelHolderEntry.getValue().isBindable()) {
|
||||
for (Map.Entry<String, BoundTargetHolder> boundTargetHolderEntry : this.outputHolders.entrySet()) {
|
||||
if (boundTargetHolderEntry.getValue().isBindable()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, channelHolderEntry.getKey()));
|
||||
log.debug(String.format("Binding %s:%s:%s", this.namespace, this.type,
|
||||
boundTargetHolderEntry.getKey()));
|
||||
}
|
||||
channelBindingService.unbindProducers(channelHolderEntry.getKey());
|
||||
bindingService.unbindProducers(boundTargetHolderEntry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,22 +270,22 @@ public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Obje
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds information about the channels exposed by the interface proxy, as well as
|
||||
* their status.
|
||||
* Holds information about the binding targets exposed by the interface proxy, as well
|
||||
* as their status.
|
||||
*/
|
||||
private final class ChannelHolder {
|
||||
private final class BoundTargetHolder {
|
||||
|
||||
private MessageChannel messageChannel;
|
||||
private Object boundTarget;
|
||||
|
||||
private boolean bindable;
|
||||
|
||||
private ChannelHolder(MessageChannel messageChannel, boolean bindable) {
|
||||
this.messageChannel = messageChannel;
|
||||
private BoundTargetHolder(Object boundTarget, boolean bindable) {
|
||||
this.boundTarget = boundTarget;
|
||||
this.bindable = bindable;
|
||||
}
|
||||
|
||||
public MessageChannel getMessageChannel() {
|
||||
return this.messageChannel;
|
||||
public Object getBoundTarget() {
|
||||
return this.boundTarget;
|
||||
}
|
||||
|
||||
public boolean isBindable() {
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.cloud.stream.binding;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binder.Binding;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.core.BeanFactoryMessageChannelDestinationResolver;
|
||||
import org.springframework.messaging.core.DestinationResolutionException;
|
||||
@@ -37,22 +37,23 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public class BinderAwareChannelResolver extends BeanFactoryMessageChannelDestinationResolver {
|
||||
|
||||
private final ChannelBindingService channelBindingService;
|
||||
private final BindingService bindingService;
|
||||
|
||||
private final BindableChannelFactory bindableChannelFactory;
|
||||
private final AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory;
|
||||
|
||||
private final DynamicDestinationsBindable dynamicDestinationsBindable;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BinderAwareChannelResolver(ChannelBindingService channelBindingService,
|
||||
BindableChannelFactory bindableChannelFactory, DynamicDestinationsBindable dynamicDestinationsBindable) {
|
||||
public BinderAwareChannelResolver(BindingService bindingService,
|
||||
AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory,
|
||||
DynamicDestinationsBindable dynamicDestinationsBindable) {
|
||||
this.dynamicDestinationsBindable = dynamicDestinationsBindable;
|
||||
Assert.notNull(channelBindingService, "'channelBindingService' cannot be null");
|
||||
Assert.notNull(bindableChannelFactory, "'bindableChannelFactory' cannot be null");
|
||||
this.channelBindingService = channelBindingService;
|
||||
this.bindableChannelFactory = bindableChannelFactory;
|
||||
Assert.notNull(bindingService, "'bindingService' cannot be null");
|
||||
Assert.notNull(bindingTargetFactory, "'bindingTargetFactory' cannot be null");
|
||||
this.bindingService = bindingService;
|
||||
this.bindingTargetFactory = bindingTargetFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,18 +77,18 @@ public class BinderAwareChannelResolver extends BeanFactoryMessageChannelDestina
|
||||
synchronized (this) {
|
||||
if (this.beanFactory != null) {
|
||||
String[] dynamicDestinations = null;
|
||||
ChannelBindingServiceProperties channelBindingServiceProperties =
|
||||
this.channelBindingService.getChannelBindingServiceProperties();
|
||||
if (channelBindingServiceProperties != null) {
|
||||
dynamicDestinations = channelBindingServiceProperties.getDynamicDestinations();
|
||||
BindingServiceProperties bindingServiceProperties = this.bindingService
|
||||
.getBindingServiceProperties();
|
||||
if (bindingServiceProperties != null) {
|
||||
dynamicDestinations = bindingServiceProperties.getDynamicDestinations();
|
||||
}
|
||||
boolean dynamicAllowed = ObjectUtils.isEmpty(dynamicDestinations)
|
||||
|| ObjectUtils.containsElement(dynamicDestinations, channelName);
|
||||
if (dynamicAllowed) {
|
||||
channel = this.bindableChannelFactory.createOutputChannel(channelName);
|
||||
channel = this.bindingTargetFactory.createOutput(channelName);
|
||||
this.beanFactory.registerSingleton(channelName, channel);
|
||||
channel = (MessageChannel) this.beanFactory.initializeBean(channel, channelName);
|
||||
Binding<MessageChannel> binding = this.channelBindingService.bindProducer(channel, channelName);
|
||||
Binding<MessageChannel> binding = this.bindingService.bindProducer(channel, channelName);
|
||||
this.dynamicDestinationsBindable.addOutputBinding(channelName, binding);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -32,88 +32,79 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class for registering bean definitions for message channels.
|
||||
* Utility class for registering bean definitions for binding targets.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class BindingBeanDefinitionRegistryUtils {
|
||||
|
||||
public static void registerInputChannelBeanDefinition(String qualifierValue,
|
||||
String name, String channelInterfaceBeanName,
|
||||
String channelInterfaceMethodName, BeanDefinitionRegistry registry) {
|
||||
registerChannelBeanDefinition(Input.class, qualifierValue, name,
|
||||
channelInterfaceBeanName, channelInterfaceMethodName, registry);
|
||||
}
|
||||
|
||||
public static void registerOutputChannelBeanDefinition(String qualifierValue,
|
||||
String name, String channelInterfaceBeanName,
|
||||
String channelInterfaceMethodName, BeanDefinitionRegistry registry) {
|
||||
registerChannelBeanDefinition(Output.class, qualifierValue, name,
|
||||
channelInterfaceBeanName, channelInterfaceMethodName, registry);
|
||||
}
|
||||
|
||||
private static void registerChannelBeanDefinition(
|
||||
Class<? extends Annotation> qualifier, String qualifierValue, String name,
|
||||
String channelInterfaceBeanName, String channelInterfaceMethodName,
|
||||
public static void registerInputBindingTargetBeanDefinition(String qualifierValue, String name,
|
||||
String bindingTargetInterfaceBeanName, String bindingTargetInterfaceMethodName,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registerBindingTargetBeanDefinition(Input.class, qualifierValue, name, bindingTargetInterfaceBeanName,
|
||||
bindingTargetInterfaceMethodName, registry);
|
||||
}
|
||||
|
||||
public static void registerOutputBindingTargetBeanDefinition(String qualifierValue, String name,
|
||||
String bindingTargetInterfaceBeanName, String bindingTargetInterfaceMethodName,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registerBindingTargetBeanDefinition(Output.class, qualifierValue, name, bindingTargetInterfaceBeanName,
|
||||
bindingTargetInterfaceMethodName, registry);
|
||||
}
|
||||
|
||||
private static void registerBindingTargetBeanDefinition(Class<? extends Annotation> qualifier,
|
||||
String qualifierValue, String name, String bindingTargetInterfaceBeanName,
|
||||
String bindingTargetInterfaceMethodName, BeanDefinitionRegistry registry) {
|
||||
|
||||
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
|
||||
rootBeanDefinition.setFactoryBeanName(channelInterfaceBeanName);
|
||||
rootBeanDefinition.setUniqueFactoryMethodName(channelInterfaceMethodName);
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(qualifier,
|
||||
qualifierValue));
|
||||
rootBeanDefinition.setFactoryBeanName(bindingTargetInterfaceBeanName);
|
||||
rootBeanDefinition.setUniqueFactoryMethodName(bindingTargetInterfaceMethodName);
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(qualifier, qualifierValue));
|
||||
registry.registerBeanDefinition(name, rootBeanDefinition);
|
||||
}
|
||||
|
||||
public static void registerChannelBeanDefinitions(Class<?> type,
|
||||
final String channelInterfaceBeanName, final BeanDefinitionRegistry registry) {
|
||||
public static void registerBindingTargetBeanDefinitions(Class<?> type, final String bindingTargetInterfaceBeanName,
|
||||
final BeanDefinitionRegistry registry) {
|
||||
ReflectionUtils.doWithMethods(type, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException,
|
||||
IllegalAccessException {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Input input = AnnotationUtils.findAnnotation(method, Input.class);
|
||||
if (input != null) {
|
||||
String name = getChannelName(input, method);
|
||||
registerInputChannelBeanDefinition(input.value(), name,
|
||||
channelInterfaceBeanName, method.getName(), registry);
|
||||
String name = getBindingTargetName(input, method);
|
||||
registerInputBindingTargetBeanDefinition(input.value(), name, bindingTargetInterfaceBeanName,
|
||||
method.getName(), registry);
|
||||
}
|
||||
Output output = AnnotationUtils.findAnnotation(method, Output.class);
|
||||
if (output != null) {
|
||||
String name = getChannelName(output, method);
|
||||
registerOutputChannelBeanDefinition(output.value(), name,
|
||||
channelInterfaceBeanName, method.getName(), registry);
|
||||
String name = getBindingTargetName(output, method);
|
||||
registerOutputBindingTargetBeanDefinition(output.value(), name, bindingTargetInterfaceBeanName,
|
||||
method.getName(), registry);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static void registerChannelsQualifiedBeanDefinitions(Class<?> parent,
|
||||
Class<?> type, final BeanDefinitionRegistry registry) {
|
||||
public static void registerBindingTargetsQualifiedBeanDefinitions(Class<?> parent, Class<?> type,
|
||||
final BeanDefinitionRegistry registry) {
|
||||
|
||||
if (type.isInterface()) {
|
||||
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(
|
||||
BindableProxyFactory.class);
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(
|
||||
Bindings.class, parent));
|
||||
rootBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
|
||||
type);
|
||||
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(BindableProxyFactory.class);
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(Bindings.class, parent));
|
||||
rootBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(type);
|
||||
registry.registerBeanDefinition(type.getName(), rootBeanDefinition);
|
||||
}
|
||||
else {
|
||||
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(type);
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(
|
||||
Bindings.class, parent));
|
||||
rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(Bindings.class, parent));
|
||||
registry.registerBeanDefinition(type.getName(), rootBeanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getChannelName(Annotation annotation, Method method) {
|
||||
Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(annotation,
|
||||
false);
|
||||
if (attrs.containsKey("value")
|
||||
&& StringUtils.hasText((CharSequence) attrs.get("value"))) {
|
||||
public static String getBindingTargetName(Annotation annotation, Method method) {
|
||||
Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(annotation, false);
|
||||
if (attrs.containsKey("value") && StringUtils.hasText((CharSequence) attrs.get("value"))) {
|
||||
return (String) attrs.get("value");
|
||||
}
|
||||
return method.getName();
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.binding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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.beans.BeanUtils;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
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.ConsumerProperties;
|
||||
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.ProducerProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.beanvalidation.CustomValidatorBean;
|
||||
|
||||
/**
|
||||
* Handles binding of input/output targets by delegating to an underlying
|
||||
* {@link Binder}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class BindingService {
|
||||
|
||||
private final CustomValidatorBean validator;
|
||||
|
||||
private final Log log = LogFactory.getLog(BindingService.class);
|
||||
|
||||
private BinderFactory binderFactory;
|
||||
|
||||
private final BindingServiceProperties bindingServiceProperties;
|
||||
|
||||
private final Map<String, Binding<?>> producerBindings = new HashMap<>();
|
||||
|
||||
private final Map<String, List<Binding<?>>> consumerBindings = new HashMap<>();
|
||||
|
||||
public BindingService(
|
||||
BindingServiceProperties bindingServiceProperties,
|
||||
BinderFactory binderFactory) {
|
||||
this.bindingServiceProperties = bindingServiceProperties;
|
||||
this.binderFactory = binderFactory;
|
||||
this.validator = new CustomValidatorBean();
|
||||
this.validator.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Collection<Binding<T>> bindConsumer(T input, String inputName) {
|
||||
String bindingTarget = this.bindingServiceProperties
|
||||
.getBindingDestination(inputName);
|
||||
String[] bindingTargets = StringUtils
|
||||
.commaDelimitedListToStringArray(bindingTarget);
|
||||
Collection<Binding<T>> bindings = new ArrayList<>();
|
||||
Binder<T, ConsumerProperties, ?> binder = (Binder<T, ConsumerProperties, ?>) getBinder(
|
||||
inputName, input.getClass());
|
||||
ConsumerProperties consumerProperties = this.bindingServiceProperties
|
||||
.getConsumerProperties(inputName);
|
||||
if (binder instanceof ExtendedPropertiesBinder) {
|
||||
Object extension = ((ExtendedPropertiesBinder) binder)
|
||||
.getExtendedConsumerProperties(inputName);
|
||||
ExtendedConsumerProperties extendedConsumerProperties = new ExtendedConsumerProperties(
|
||||
extension);
|
||||
BeanUtils.copyProperties(consumerProperties, extendedConsumerProperties);
|
||||
consumerProperties = extendedConsumerProperties;
|
||||
}
|
||||
validate(consumerProperties);
|
||||
for (String target : bindingTargets) {
|
||||
Binding<T> binding = binder.bindConsumer(target,
|
||||
bindingServiceProperties.getGroup(inputName), input,
|
||||
consumerProperties);
|
||||
bindings.add(binding);
|
||||
}
|
||||
bindings = Collections.unmodifiableCollection(bindings);
|
||||
this.consumerBindings.put(inputName, new ArrayList<Binding<?>>(bindings));
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Binding<T> bindProducer(T output, String outputName) {
|
||||
String bindingTarget = this.bindingServiceProperties
|
||||
.getBindingDestination(outputName);
|
||||
Binder<T, ?, ProducerProperties> binder = (Binder<T, ?, ProducerProperties>) getBinder(
|
||||
outputName, output.getClass());
|
||||
ProducerProperties producerProperties = this.bindingServiceProperties
|
||||
.getProducerProperties(outputName);
|
||||
if (binder instanceof ExtendedPropertiesBinder) {
|
||||
Object extension = ((ExtendedPropertiesBinder) binder)
|
||||
.getExtendedProducerProperties(outputName);
|
||||
ExtendedProducerProperties extendedProducerProperties = new ExtendedProducerProperties<>(
|
||||
extension);
|
||||
BeanUtils.copyProperties(producerProperties, extendedProducerProperties);
|
||||
producerProperties = extendedProducerProperties;
|
||||
}
|
||||
validate(producerProperties);
|
||||
Binding<T> binding = binder.bindProducer(bindingTarget, output,
|
||||
producerProperties);
|
||||
this.producerBindings.put(outputName, binding);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public void unbindConsumers(String inputName) {
|
||||
List<Binding<?>> bindings = this.consumerBindings.remove(inputName);
|
||||
if (bindings != null && !CollectionUtils.isEmpty(bindings)) {
|
||||
for (Binding<?> binding : bindings) {
|
||||
binding.unbind();
|
||||
}
|
||||
}
|
||||
else if (log.isWarnEnabled()) {
|
||||
log.warn("Trying to unbind '" + inputName + "', but no binding found.");
|
||||
}
|
||||
}
|
||||
|
||||
public void unbindProducers(String outputName) {
|
||||
Binding<?> binding = this.producerBindings.remove(outputName);
|
||||
if (binding != null) {
|
||||
binding.unbind();
|
||||
}
|
||||
else if (log.isWarnEnabled()) {
|
||||
log.warn("Trying to unbind '" + outputName + "', but no binding found.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Binder<T, ?, ?> getBinder(String channelName, Class<T> bindableType) {
|
||||
String transport = this.bindingServiceProperties.getBinder(channelName);
|
||||
return binderFactory.getBinder(transport, bindableType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provided for backwards compatibility. Will be removed in a future version.
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public BindingServiceProperties getChannelBindingServiceProperties() {
|
||||
return this.bindingServiceProperties;
|
||||
}
|
||||
|
||||
public BindingServiceProperties getBindingServiceProperties() {
|
||||
return this.bindingServiceProperties;
|
||||
}
|
||||
|
||||
private void validate(Object properties) {
|
||||
RelaxedDataBinder dataBinder = new RelaxedDataBinder(properties);
|
||||
dataBinder.setValidator(validator);
|
||||
dataBinder.validate();
|
||||
if (dataBinder.getBindingResult().hasErrors()) {
|
||||
throw new IllegalStateException(dataBinder.getBindingResult().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.binding;
|
||||
|
||||
/**
|
||||
* Defines methods to create/configure the binding targets defined by
|
||||
* {@link org.springframework.cloud.stream.annotation.EnableBinding}.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public interface BindingTargetFactory {
|
||||
|
||||
/**
|
||||
* Checks whether a specific binding target type can be created by this factory.
|
||||
* @param clazz the binding target type
|
||||
* @return true if the binding target can be created
|
||||
*/
|
||||
boolean canCreate(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Create an input binding target that will be bound via a corresponding
|
||||
* {@link org.springframework.cloud.stream.binder.Binder}.
|
||||
* @param name name of the binding target
|
||||
* @return binding target
|
||||
*/
|
||||
Object createInput(String name);
|
||||
|
||||
/**
|
||||
* Create an output binding target that will be bound via a corresponding
|
||||
* {@link org.springframework.cloud.stream.binder.Binder}.
|
||||
* @param name name of the binding target
|
||||
* @return binding target
|
||||
*/
|
||||
Object createOutput(String name);
|
||||
|
||||
}
|
||||
@@ -1,157 +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.binding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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.beans.BeanUtils;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
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.ConsumerProperties;
|
||||
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.ProducerProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.beanvalidation.CustomValidatorBean;
|
||||
|
||||
/**
|
||||
* Handles the operations related to channel binding including binding of input/output channels by delegating
|
||||
* to an underlying {@link Binder}, setting up data type conversion for binding channel.
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class ChannelBindingService {
|
||||
|
||||
private final CustomValidatorBean validator;
|
||||
|
||||
private final Log log = LogFactory.getLog(ChannelBindingService.class);
|
||||
|
||||
private BinderFactory<MessageChannel> binderFactory;
|
||||
|
||||
private final ChannelBindingServiceProperties channelBindingServiceProperties;
|
||||
|
||||
private final Map<String, Binding<MessageChannel>> producerBindings = new HashMap<>();
|
||||
|
||||
private final Map<String, List<Binding<MessageChannel>>> consumerBindings = new HashMap<>();
|
||||
|
||||
public ChannelBindingService(ChannelBindingServiceProperties channelBindingServiceProperties,
|
||||
BinderFactory<MessageChannel> binderFactory) {
|
||||
this.channelBindingServiceProperties = channelBindingServiceProperties;
|
||||
this.binderFactory = binderFactory;
|
||||
this.validator = new CustomValidatorBean();
|
||||
this.validator.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<Binding<MessageChannel>> bindConsumer(MessageChannel inputChannel, String inputChannelName) {
|
||||
String channelBindingTarget = this.channelBindingServiceProperties.getBindingDestination(inputChannelName);
|
||||
String[] channelBindingTargets = StringUtils.commaDelimitedListToStringArray(channelBindingTarget);
|
||||
List<Binding<MessageChannel>> bindings = new ArrayList<>();
|
||||
Binder<MessageChannel, ConsumerProperties, ?> binder =
|
||||
(Binder<MessageChannel, ConsumerProperties, ?>) getBinderForChannel(inputChannelName);
|
||||
ConsumerProperties consumerProperties =
|
||||
this.channelBindingServiceProperties.getConsumerProperties(inputChannelName);
|
||||
if (binder instanceof ExtendedPropertiesBinder) {
|
||||
Object extension = ((ExtendedPropertiesBinder) binder).getExtendedConsumerProperties(inputChannelName);
|
||||
ExtendedConsumerProperties extendedConsumerProperties = new ExtendedConsumerProperties(extension);
|
||||
BeanUtils.copyProperties(consumerProperties, extendedConsumerProperties);
|
||||
consumerProperties = extendedConsumerProperties;
|
||||
}
|
||||
validate(consumerProperties);
|
||||
for (String target : channelBindingTargets) {
|
||||
Binding<MessageChannel> binding = binder.bindConsumer(target, channelBindingServiceProperties.getGroup(inputChannelName), inputChannel, consumerProperties);
|
||||
bindings.add(binding);
|
||||
}
|
||||
this.consumerBindings.put(inputChannelName, bindings);
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Binding<MessageChannel> bindProducer(MessageChannel outputChannel, String outputChannelName) {
|
||||
String channelBindingTarget = this.channelBindingServiceProperties.getBindingDestination(outputChannelName);
|
||||
Binder<MessageChannel, ?, ProducerProperties> binder =
|
||||
(Binder<MessageChannel, ?, ProducerProperties>) getBinderForChannel(outputChannelName);
|
||||
ProducerProperties producerProperties = this.channelBindingServiceProperties.getProducerProperties(outputChannelName);
|
||||
if (binder instanceof ExtendedPropertiesBinder) {
|
||||
Object extension = ((ExtendedPropertiesBinder) binder).getExtendedProducerProperties(outputChannelName);
|
||||
ExtendedProducerProperties extendedProducerProperties = new ExtendedProducerProperties<>(extension);
|
||||
BeanUtils.copyProperties(producerProperties, extendedProducerProperties);
|
||||
producerProperties = extendedProducerProperties;
|
||||
}
|
||||
validate(producerProperties);
|
||||
Binding<MessageChannel> binding = binder.bindProducer(channelBindingTarget, outputChannel, producerProperties);
|
||||
this.producerBindings.put(outputChannelName, binding);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public void unbindConsumers(String inputChannelName) {
|
||||
List<Binding<MessageChannel>> bindings = this.consumerBindings.remove(inputChannelName);
|
||||
if (bindings != null && !CollectionUtils.isEmpty(bindings)) {
|
||||
for (Binding<MessageChannel> binding : bindings) {
|
||||
binding.unbind();
|
||||
}
|
||||
}
|
||||
else if (log.isWarnEnabled()) {
|
||||
log.warn("Trying to unbind channel '" + inputChannelName + "', but no binding found.");
|
||||
}
|
||||
}
|
||||
|
||||
public void unbindProducers(String outputChannelName) {
|
||||
Binding<MessageChannel> binding = this.producerBindings.remove(outputChannelName);
|
||||
if (binding != null) {
|
||||
binding.unbind();
|
||||
}
|
||||
else if (log.isWarnEnabled()) {
|
||||
log.warn("Trying to unbind channel '" + outputChannelName + "', but no binding found.");
|
||||
}
|
||||
}
|
||||
|
||||
private Binder<MessageChannel, ?, ?> getBinderForChannel(String channelName) {
|
||||
String transport = this.channelBindingServiceProperties.getBinder(channelName);
|
||||
return binderFactory.getBinder(transport);
|
||||
}
|
||||
|
||||
public ChannelBindingServiceProperties getChannelBindingServiceProperties() {
|
||||
return this.channelBindingServiceProperties;
|
||||
}
|
||||
|
||||
private void validate(Object properties) {
|
||||
RelaxedDataBinder dataBinder = new RelaxedDataBinder(properties);
|
||||
dataBinder.setValidator(validator);
|
||||
dataBinder.validate();
|
||||
if (dataBinder.getBindingResult().hasErrors()) {
|
||||
throw new IllegalStateException(dataBinder.getBindingResult().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public final class DynamicDestinationsBindable extends BindableAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindOutputs(ChannelBindingService adapter) {
|
||||
public void unbindOutputs(BindingService adapter) {
|
||||
for (Map.Entry<String, Binding> entry: outputBindings.entrySet()) {
|
||||
entry.getValue().unbind();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* Coordinates binding/unbinding of input channels in accordance to the lifecycle
|
||||
* Coordinates binding/unbinding of input binding targets in accordance to the lifecycle
|
||||
* of the host context.
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
@@ -45,14 +45,14 @@ public class InputBindingLifecycle implements SmartLifecycle, ApplicationContext
|
||||
@Override
|
||||
public void start() {
|
||||
if (!running) {
|
||||
// retrieve the ChannelBindingService lazily, avoiding early initialization
|
||||
// retrieve the BindingService lazily, avoiding early initialization
|
||||
try {
|
||||
ChannelBindingService channelBindingService = this.applicationContext
|
||||
.getBean(ChannelBindingService.class);
|
||||
BindingService bindingService = this.applicationContext
|
||||
.getBean(BindingService.class);
|
||||
Map<String, Bindable> bindables = this.applicationContext
|
||||
.getBeansOfType(Bindable.class);
|
||||
for (Bindable bindable : bindables.values()) {
|
||||
bindable.bindInputs(channelBindingService);
|
||||
bindable.bindInputs(bindingService);
|
||||
}
|
||||
}
|
||||
catch (BeansException e) {
|
||||
@@ -67,14 +67,14 @@ public class InputBindingLifecycle implements SmartLifecycle, ApplicationContext
|
||||
public void stop() {
|
||||
if (running) {
|
||||
try {
|
||||
// retrieve the ChannelBindingService lazily, avoiding early
|
||||
// retrieve the BindingService lazily, avoiding early
|
||||
// initialization
|
||||
ChannelBindingService channelBindingService = this.applicationContext
|
||||
.getBean(ChannelBindingService.class);
|
||||
BindingService bindingService = this.applicationContext
|
||||
.getBean(BindingService.class);
|
||||
Map<String, Bindable> bindables = this.applicationContext
|
||||
.getBeansOfType(Bindable.class);
|
||||
for (Bindable bindable : bindables.values()) {
|
||||
bindable.unbindInputs(channelBindingService);
|
||||
bindable.unbindInputs(bindingService);
|
||||
}
|
||||
}
|
||||
catch (BeansException e) {
|
||||
|
||||
@@ -29,15 +29,15 @@ public class MessageChannelStreamListenerResultAdapter
|
||||
implements StreamListenerResultAdapter<MessageChannel, MessageChannel> {
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> resultType, Class<?> boundElement) {
|
||||
public boolean supports(Class<?> resultType, Class<?> bindingTarget) {
|
||||
return MessageChannel.class.isAssignableFrom(resultType)
|
||||
&& MessageChannel.class.isAssignableFrom(boundElement);
|
||||
&& MessageChannel.class.isAssignableFrom(bindingTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adapt(MessageChannel streamListenerResult, MessageChannel boundElement) {
|
||||
public void adapt(MessageChannel streamListenerResult, MessageChannel bindingTarget) {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(boundElement);
|
||||
handler.setOutputChannel(bindingTarget);
|
||||
handler.afterPropertiesSet();
|
||||
((SubscribableChannel) streamListenerResult).subscribe(handler);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binder.BinderHeaders;
|
||||
import org.springframework.cloud.stream.binder.PartitionHandler;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.cloud.stream.converter.MessageConverterUtils;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
@@ -63,12 +63,12 @@ public class MessageConverterConfigurer implements MessageChannelConfigurer, Bea
|
||||
|
||||
private final CompositeMessageConverterFactory compositeMessageConverterFactory;
|
||||
|
||||
private final ChannelBindingServiceProperties channelBindingServiceProperties;
|
||||
private final BindingServiceProperties bindingServiceProperties;
|
||||
|
||||
public MessageConverterConfigurer(ChannelBindingServiceProperties channelBindingServiceProperties,
|
||||
public MessageConverterConfigurer(BindingServiceProperties bindingServiceProperties,
|
||||
CompositeMessageConverterFactory compositeMessageConverterFactory) {
|
||||
Assert.notNull(compositeMessageConverterFactory, "The message converter factory cannot be null");
|
||||
this.channelBindingServiceProperties = channelBindingServiceProperties;
|
||||
this.bindingServiceProperties = bindingServiceProperties;
|
||||
this.compositeMessageConverterFactory = compositeMessageConverterFactory;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class MessageConverterConfigurer implements MessageChannelConfigurer, Bea
|
||||
private void configureMessageChannel(MessageChannel channel, String channelName, boolean input) {
|
||||
Assert.isAssignable(AbstractMessageChannel.class, channel.getClass());
|
||||
AbstractMessageChannel messageChannel = (AbstractMessageChannel) channel;
|
||||
final BindingProperties bindingProperties = this.channelBindingServiceProperties.getBindingProperties(
|
||||
final BindingProperties bindingProperties = this.bindingServiceProperties.getBindingProperties(
|
||||
channelName);
|
||||
final String contentType = bindingProperties.getContentType();
|
||||
if (!input && bindingProperties.getProducer() != null && bindingProperties.getProducer().isPartitioned()) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* Coordinates binding/unbinding of output channels in accordance to the lifecycle
|
||||
* Coordinates binding/unbinding of output binding targets in accordance to the lifecycle
|
||||
* of the host context.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
@@ -47,14 +47,14 @@ public class OutputBindingLifecycle implements SmartLifecycle, ApplicationContex
|
||||
public void start() {
|
||||
if (!running) {
|
||||
|
||||
// retrieve the ChannelBindingService lazily, avoiding early initialization
|
||||
// retrieve the BindingService lazily, avoiding early initialization
|
||||
try {
|
||||
ChannelBindingService channelBindingService = this.applicationContext
|
||||
.getBean(ChannelBindingService.class);
|
||||
BindingService bindingService = this.applicationContext
|
||||
.getBean(BindingService.class);
|
||||
Map<String, Bindable> bindables = this.applicationContext
|
||||
.getBeansOfType(Bindable.class);
|
||||
for (Bindable bindable : bindables.values()) {
|
||||
bindable.bindOutputs(channelBindingService);
|
||||
bindable.bindOutputs(bindingService);
|
||||
}
|
||||
}
|
||||
catch (BeansException e) {
|
||||
@@ -69,14 +69,14 @@ public class OutputBindingLifecycle implements SmartLifecycle, ApplicationContex
|
||||
public void stop() {
|
||||
if (running) {
|
||||
try {
|
||||
// retrieve the ChannelBindingService lazily, avoiding early
|
||||
// retrieve the BindingService lazily, avoiding early
|
||||
// initialization
|
||||
ChannelBindingService channelBindingService = this.applicationContext
|
||||
.getBean(ChannelBindingService.class);
|
||||
BindingService bindingService = this.applicationContext
|
||||
.getBean(BindingService.class);
|
||||
Map<String, Bindable> bindables = this.applicationContext
|
||||
.getBeansOfType(Bindable.class);
|
||||
for (Bindable bindable : bindables.values()) {
|
||||
bindable.unbindOutputs(channelBindingService);
|
||||
bindable.unbindOutputs(bindingService);
|
||||
}
|
||||
}
|
||||
catch (BeansException e) {
|
||||
@@ -106,7 +106,8 @@ public class OutputBindingLifecycle implements SmartLifecycle, ApplicationContex
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a low value so that this bean is started after receiving Lifecycle beans are started. Beans that need to start before bindings will set a lower phase value.
|
||||
* Return a low value so that this bean is started after receiving Lifecycle beans are
|
||||
* started. Beans that need to start before bindings will set a lower phase value.
|
||||
*/
|
||||
@Override
|
||||
public int getPhase() {
|
||||
|
||||
@@ -21,38 +21,37 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* A {@link Bindable} component that wraps a generic channel. Useful for binding channels outside the
|
||||
* {@link org.springframework.cloud.stream.annotation.Input} and {@link org.springframework.cloud.stream.annotation.Output}
|
||||
* annotated interfaces.
|
||||
* A {@link Bindable} component that wraps a generic output binding target. Useful for
|
||||
* binding targets outside the {@link org.springframework.cloud.stream.annotation.Input}
|
||||
* and {@link org.springframework.cloud.stream.annotation.Output} annotated interfaces.
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class SingleChannelBindable extends BindableAdapter {
|
||||
public class SingleBindingTargetBindable<T> extends BindableAdapter {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final MessageChannel messageChannel;
|
||||
private final T bindingTarget;
|
||||
|
||||
public SingleChannelBindable(String name, MessageChannel messageChannel) {
|
||||
public SingleBindingTargetBindable(String name, T bindingTarget) {
|
||||
this.name = name;
|
||||
this.messageChannel = messageChannel;
|
||||
this.bindingTarget = bindingTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindOutputs(ChannelBindingService adapter) {
|
||||
adapter.bindProducer(messageChannel, name);
|
||||
public void bindOutputs(BindingService bindingService) {
|
||||
bindingService.bindProducer(bindingTarget, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindOutputs(ChannelBindingService adapter) {
|
||||
adapter.unbindProducers(name);
|
||||
public void unbindOutputs(BindingService bindingService) {
|
||||
bindingService.unbindProducers(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getOutputs() {
|
||||
return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(name)));
|
||||
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(name)));
|
||||
}
|
||||
}
|
||||
@@ -79,16 +79,16 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
|
||||
Map<String, StreamListenerParameterAdapter> parameterAdapterMap =
|
||||
this.applicationContext.getBeansOfType(StreamListenerParameterAdapter.class);
|
||||
Map<String, StreamListenerParameterAdapter> parameterAdapterMap = this.applicationContext
|
||||
.getBeansOfType(StreamListenerParameterAdapter.class);
|
||||
for (StreamListenerParameterAdapter parameterAdapter : parameterAdapterMap.values()) {
|
||||
this.streamListenerParameterAdapters.add(parameterAdapter);
|
||||
}
|
||||
Map<String, StreamListenerResultAdapter> resultAdapterMap =
|
||||
this.applicationContext.getBeansOfType(StreamListenerResultAdapter.class);
|
||||
Map<String, StreamListenerResultAdapter> resultAdapterMap = this.applicationContext
|
||||
.getBeansOfType(StreamListenerResultAdapter.class);
|
||||
this.streamListenerResultAdapters.add(new MessageChannelStreamListenerResultAdapter());
|
||||
for (StreamListenerResultAdapter resultAdapter : resultAdapterMap.values()) {
|
||||
this.streamListenerResultAdapters.add(resultAdapter);
|
||||
@@ -108,24 +108,30 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
public void doWith(final Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
StreamListener streamListener = AnnotationUtils.findAnnotation(method, StreamListener.class);
|
||||
if (streamListener != null) {
|
||||
Assert.isTrue(method.getAnnotation(Input.class) == null, StreamListenerErrorMessages.INPUT_AT_STREAM_LISTENER);
|
||||
Assert.isTrue(method.getAnnotation(Input.class) == null,
|
||||
StreamListenerErrorMessages.INPUT_AT_STREAM_LISTENER);
|
||||
String methodAnnotatedInboundName = streamListener.value();
|
||||
String methodAnnotatedOutboundName = StreamListenerMethodUtils.getOutboundElementNameFromMethod(method);
|
||||
String methodAnnotatedOutboundName = StreamListenerMethodUtils.getOutboundBindingTargetName(method);
|
||||
int inputAnnotationCount = StreamListenerMethodUtils.inputAnnotationCount(method);
|
||||
int outputAnnotationCount = StreamListenerMethodUtils.outputAnnotationCount(method);
|
||||
boolean isDeclarative = checkDeclarativeMethod(method, methodAnnotatedInboundName, methodAnnotatedOutboundName);
|
||||
StreamListenerMethodUtils.validateStreamListenerMethod(method, inputAnnotationCount, outputAnnotationCount,
|
||||
methodAnnotatedInboundName, methodAnnotatedOutboundName, isDeclarative);
|
||||
boolean isDeclarative = checkDeclarativeMethod(method, methodAnnotatedInboundName,
|
||||
methodAnnotatedOutboundName);
|
||||
StreamListenerMethodUtils.validateStreamListenerMethod(method, inputAnnotationCount,
|
||||
outputAnnotationCount, methodAnnotatedInboundName, methodAnnotatedOutboundName,
|
||||
isDeclarative);
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
if (!StringUtils.hasText(methodAnnotatedOutboundName)) {
|
||||
if (outputAnnotationCount == 0) {
|
||||
throw new IllegalArgumentException(StreamListenerErrorMessages.RETURN_TYPE_NO_OUTBOUND_SPECIFIED);
|
||||
throw new IllegalArgumentException(
|
||||
StreamListenerErrorMessages.RETURN_TYPE_NO_OUTBOUND_SPECIFIED);
|
||||
}
|
||||
Assert.isTrue((outputAnnotationCount == 1), StreamListenerErrorMessages.RETURN_TYPE_MULTIPLE_OUTBOUND_SPECIFIED);
|
||||
Assert.isTrue((outputAnnotationCount == 1),
|
||||
StreamListenerErrorMessages.RETURN_TYPE_MULTIPLE_OUTBOUND_SPECIFIED);
|
||||
}
|
||||
}
|
||||
if (isDeclarative) {
|
||||
invokeSetupMethodOnListenedChannel(method, bean, methodAnnotatedInboundName, methodAnnotatedOutboundName);
|
||||
invokeSetupMethodOnListenedChannel(method, bean, methodAnnotatedInboundName,
|
||||
methodAnnotatedOutboundName);
|
||||
}
|
||||
else {
|
||||
registerHandlerMethodOnListenedChannel(method, streamListener, bean);
|
||||
@@ -136,34 +142,36 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
return bean;
|
||||
}
|
||||
|
||||
private boolean checkDeclarativeMethod(Method method, String methodAnnotatedInboundName, String methodAnnotatedOutboundName) {
|
||||
private boolean checkDeclarativeMethod(Method method, String methodAnnotatedInboundName,
|
||||
String methodAnnotatedOutboundName) {
|
||||
int methodArgumentsLength = method.getParameterTypes().length;
|
||||
for (int parameterIndex = 0; parameterIndex < methodArgumentsLength; parameterIndex++) {
|
||||
MethodParameter methodParameter = MethodParameter
|
||||
.forMethodOrConstructor(method, parameterIndex);
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, parameterIndex);
|
||||
if (methodParameter.hasParameterAnnotation(Input.class)) {
|
||||
String inboundName = (String) AnnotationUtils
|
||||
.getValue(methodParameter.getParameterAnnotation(Input.class));
|
||||
Assert.isTrue(StringUtils.hasText(inboundName), StreamListenerErrorMessages.INVALID_INBOUND_NAME);
|
||||
Assert.isTrue(isDeclarativeMethodParameter(this.applicationContext.getBean(inboundName),
|
||||
methodParameter), StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
Assert.isTrue(
|
||||
isDeclarativeMethodParameter(this.applicationContext.getBean(inboundName), methodParameter),
|
||||
StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
return true;
|
||||
}
|
||||
if (methodParameter.hasParameterAnnotation(Output.class)) {
|
||||
String outboundName = (String) AnnotationUtils
|
||||
.getValue(methodParameter.getParameterAnnotation(Output.class));
|
||||
Assert.isTrue(StringUtils.hasText(outboundName), StreamListenerErrorMessages.INVALID_OUTBOUND_NAME);
|
||||
Assert.isTrue(isDeclarativeMethodParameter(this.applicationContext.getBean(outboundName),
|
||||
methodParameter), StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
Assert.isTrue(
|
||||
isDeclarativeMethodParameter(this.applicationContext.getBean(outboundName), methodParameter),
|
||||
StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
return true;
|
||||
}
|
||||
if (StringUtils.hasText(methodAnnotatedOutboundName)) {
|
||||
return isDeclarativeMethodParameter(
|
||||
this.applicationContext.getBean(methodAnnotatedOutboundName), methodParameter);
|
||||
return isDeclarativeMethodParameter(this.applicationContext.getBean(methodAnnotatedOutboundName),
|
||||
methodParameter);
|
||||
}
|
||||
if (StringUtils.hasText(methodAnnotatedInboundName)) {
|
||||
return isDeclarativeMethodParameter(
|
||||
this.applicationContext.getBean(methodAnnotatedInboundName), methodParameter);
|
||||
return isDeclarativeMethodParameter(this.applicationContext.getBean(methodAnnotatedInboundName),
|
||||
methodParameter);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -183,8 +191,9 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private void invokeSetupMethodOnListenedChannel(Method method, Object bean, String inboundName, String outboundName) {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void invokeSetupMethodOnListenedChannel(Method method, Object bean, String inboundName,
|
||||
String outboundName) {
|
||||
Object[] arguments = new Object[method.getParameterTypes().length];
|
||||
for (int parameterIndex = 0; parameterIndex < arguments.length; parameterIndex++) {
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, parameterIndex);
|
||||
@@ -206,17 +215,16 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
arguments[parameterIndex] = targetBean;
|
||||
}
|
||||
else {
|
||||
for (StreamListenerParameterAdapter<?, Object> streamListenerParameterAdapter :
|
||||
this.streamListenerParameterAdapters) {
|
||||
for (StreamListenerParameterAdapter<?, Object> streamListenerParameterAdapter : this.streamListenerParameterAdapters) {
|
||||
if (streamListenerParameterAdapter.supports(targetBean.getClass(), methodParameter)) {
|
||||
arguments[parameterIndex] = streamListenerParameterAdapter.adapt(targetBean, methodParameter);
|
||||
arguments[parameterIndex] = streamListenerParameterAdapter.adapt(targetBean,
|
||||
methodParameter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(arguments[parameterIndex],
|
||||
"Cannot convert argument " + parameterIndex + " of " + method + "from " + targetBean.getClass()
|
||||
+ " to " + parameterType);
|
||||
Assert.notNull(arguments[parameterIndex], "Cannot convert argument " + parameterIndex + " of " + method
|
||||
+ "from " + targetBean.getClass() + " to " + parameterType);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
@@ -230,15 +238,15 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
Object result = method.invoke(bean, arguments);
|
||||
if (!StringUtils.hasText(outboundName)) {
|
||||
for (int parameterIndex = 0; parameterIndex < method.getParameterTypes().length; parameterIndex++) {
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, parameterIndex);
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method,
|
||||
parameterIndex);
|
||||
if (methodParameter.hasParameterAnnotation(Output.class)) {
|
||||
outboundName = methodParameter.getParameterAnnotation(Output.class).value();
|
||||
}
|
||||
}
|
||||
}
|
||||
Object targetBean = this.applicationContext.getBean(outboundName);
|
||||
for (StreamListenerResultAdapter streamListenerResultAdapter : this
|
||||
.streamListenerResultAdapters) {
|
||||
for (StreamListenerResultAdapter streamListenerResultAdapter : this.streamListenerResultAdapters) {
|
||||
if (streamListenerResultAdapter.supports(result.getClass(), targetBean.getClass())) {
|
||||
streamListenerResultAdapter.adapt(result, targetBean);
|
||||
break;
|
||||
@@ -254,31 +262,29 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
protected void registerHandlerMethodOnListenedChannel(Method method, StreamListener streamListener, Object bean) {
|
||||
Method targetMethod = checkProxy(method, bean);
|
||||
Assert.hasText(streamListener.value(), "The binding name cannot be null");
|
||||
final InvocableHandlerMethod invocableHandlerMethod =
|
||||
this.messageHandlerMethodFactory.createInvocableHandlerMethod(bean, targetMethod);
|
||||
final InvocableHandlerMethod invocableHandlerMethod = this.messageHandlerMethodFactory
|
||||
.createInvocableHandlerMethod(bean, targetMethod);
|
||||
if (!StringUtils.hasText(streamListener.value())) {
|
||||
throw new BeanInitializationException("A bound component name must be specified");
|
||||
}
|
||||
if (this.mappedBindings.containsKey(streamListener.value())) {
|
||||
throw new BeanInitializationException("Duplicate @" + StreamListener.class.getSimpleName() +
|
||||
" mapping for '" + streamListener.value() + "' on " + invocableHandlerMethod.getShortLogMessage() +
|
||||
" already existing for " + this.mappedBindings.get(streamListener.value()).getShortLogMessage());
|
||||
throw new BeanInitializationException("Duplicate @" + StreamListener.class.getSimpleName()
|
||||
+ " mapping for '" + streamListener.value() + "' on " + invocableHandlerMethod.getShortLogMessage()
|
||||
+ " already existing for " + this.mappedBindings.get(streamListener.value()).getShortLogMessage());
|
||||
}
|
||||
this.mappedBindings.put(streamListener.value(), invocableHandlerMethod);
|
||||
SubscribableChannel channel = this.applicationContext.getBean(streamListener.value(),
|
||||
SubscribableChannel.class);
|
||||
final String defaultOutputChannel = StreamListenerMethodUtils.getOutboundElementNameFromMethod(method);
|
||||
final String defaultOutputChannel = StreamListenerMethodUtils.getOutboundBindingTargetName(method);
|
||||
if (invocableHandlerMethod.isVoid()) {
|
||||
Assert.isTrue(StringUtils.isEmpty(defaultOutputChannel),
|
||||
"An output channel cannot be specified for a method that " +
|
||||
"does not return a value");
|
||||
"An output channel cannot be specified for a method that does not return a value");
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(!StringUtils.isEmpty(defaultOutputChannel),
|
||||
"An output channel must be specified for a method that " +
|
||||
"can return a value");
|
||||
"An output channel must be specified for a method that can return a value");
|
||||
}
|
||||
StreamListenerMethodUtils.assertStreamListenerMessageHandlerMethod(method);
|
||||
StreamListenerMethodUtils.validateStreamListenerMessageHandler(method);
|
||||
StreamListenerMessageHandler handler = new StreamListenerMessageHandler(invocableHandlerMethod);
|
||||
handler.setApplicationContext(this.applicationContext);
|
||||
handler.setChannelResolver(this.binderAwareChannelResolver);
|
||||
@@ -291,7 +297,8 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// Dump the mappings after the context has been created, ensuring that beans can be processed correctly
|
||||
// Dump the mappings after the context has been created, ensuring that beans can
|
||||
// be processed correctly
|
||||
// again.
|
||||
this.mappedBindings.clear();
|
||||
}
|
||||
@@ -300,7 +307,8 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
Method method = methodArg;
|
||||
if (AopUtils.isJdkDynamicProxy(bean)) {
|
||||
try {
|
||||
// Found a @StreamListener method on the target class for this JDK proxy ->
|
||||
// Found a @StreamListener method on the target class for this JDK proxy
|
||||
// ->
|
||||
// is it also present on the proxy itself?
|
||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
Class<?>[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces();
|
||||
@@ -318,11 +326,11 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"@StreamListener method '%s' found on bean target class '%s', " +
|
||||
"but not found in any interface(s) for bean JDK proxy. Either " +
|
||||
"pull the method up to an interface or switch to subclass (CGLIB) " +
|
||||
"proxies by setting proxy-target-class/proxyTargetClass " +
|
||||
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()), ex);
|
||||
"@StreamListener method '%s' found on bean target class '%s', "
|
||||
+ "but not found in any interface(s) for bean JDK proxy. Either "
|
||||
+ "pull the method up to an interface or switch to subclass (CGLIB) "
|
||||
+ "proxies by setting proxy-target-class/proxyTargetClass attribute to 'true'",
|
||||
method.getName(), method.getDeclaringClass().getSimpleName()), ex);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
@@ -351,9 +359,8 @@ public class StreamListenerAnnotationBeanPostProcessor
|
||||
throw (MessagingException) e;
|
||||
}
|
||||
else {
|
||||
throw new MessagingException(requestMessage, "Exception thrown while invoking " + this
|
||||
.invocableHandlerMethod
|
||||
.getShortLogMessage(), e);
|
||||
throw new MessagingException(requestMessage,
|
||||
"Exception thrown while invoking " + this.invocableHandlerMethod.getShortLogMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.binding;
|
||||
|
||||
/**
|
||||
@@ -48,7 +49,7 @@ public abstract class StreamListenerErrorMessages {
|
||||
public static final String NO_INPUT_DESTINATION = "No input destination is configured. Use either the @StreamListener value or @Input";
|
||||
|
||||
public static final String INVALID_DECLARATIVE_METHOD_PARAMETERS = PREFIX
|
||||
+ "may use @Input or @Output annotations only in declarative mode and for parameters that are bound elements or convertible from bound elements.";
|
||||
+ "may use @Input or @Output annotations only in declarative mode and for parameters that are binding targets or convertible from binding targets.";
|
||||
|
||||
public static final String AMBIGUOUS_MESSAGE_HANDLER_METHOD_ARGUMENTS = "Ambiguous method arguments for the StreamListener method";
|
||||
|
||||
@@ -59,6 +60,4 @@ public abstract class StreamListenerErrorMessages {
|
||||
|
||||
public static final String INVALID_OUTPUT_VALUES = "Cannot set both output (@Output/@SendTo) method annotation value"
|
||||
+ " and @Output annotation as a method parameter";
|
||||
|
||||
public static final String TARGET_BEAN_NOT_EXISTS = "Target bean doesn't exist for the bound element name";
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.binding;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -29,7 +30,8 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* This class contains utility methods for handling {@link StreamListener} annotated bean methods.
|
||||
* This class contains utility methods for handling {@link StreamListener} annotated bean
|
||||
* methods.
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
@@ -57,17 +59,22 @@ public class StreamListenerMethodUtils {
|
||||
return outputAnnotationCount;
|
||||
}
|
||||
|
||||
protected static void validateStreamListenerMethod(Method method, int inputAnnotationCount, int outputAnnotationCount, String methodAnnotatedInboundName, String methodAnnotatedOutboundName, boolean isDeclarative) {
|
||||
protected static void validateStreamListenerMethod(Method method, int inputAnnotationCount,
|
||||
int outputAnnotationCount, String methodAnnotatedInboundName, String methodAnnotatedOutboundName,
|
||||
boolean isDeclarative) {
|
||||
int methodArgumentsLength = method.getParameterTypes().length;
|
||||
if (!isDeclarative) {
|
||||
Assert.isTrue(inputAnnotationCount == 0 && outputAnnotationCount == 0, StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
Assert.isTrue(inputAnnotationCount == 0 && outputAnnotationCount == 0,
|
||||
StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
}
|
||||
if (StringUtils.hasText(methodAnnotatedInboundName) && StringUtils.hasText(methodAnnotatedOutboundName)) {
|
||||
Assert.isTrue(inputAnnotationCount == 0 && outputAnnotationCount == 0, StreamListenerErrorMessages.INVALID_INPUT_OUTPUT_METHOD_PARAMETERS);
|
||||
Assert.isTrue(inputAnnotationCount == 0 && outputAnnotationCount == 0,
|
||||
StreamListenerErrorMessages.INVALID_INPUT_OUTPUT_METHOD_PARAMETERS);
|
||||
}
|
||||
if (StringUtils.hasText(methodAnnotatedInboundName)) {
|
||||
Assert.isTrue(inputAnnotationCount == 0, StreamListenerErrorMessages.INVALID_INPUT_VALUES);
|
||||
Assert.isTrue(outputAnnotationCount == 0, StreamListenerErrorMessages.INVALID_INPUT_VALUE_WITH_OUTPUT_METHOD_PARAM);
|
||||
Assert.isTrue(outputAnnotationCount == 0,
|
||||
StreamListenerErrorMessages.INVALID_INPUT_VALUE_WITH_OUTPUT_METHOD_PARAM);
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(inputAnnotationCount >= 1, StreamListenerErrorMessages.NO_INPUT_DESTINATION);
|
||||
@@ -79,21 +86,24 @@ public class StreamListenerMethodUtils {
|
||||
for (int parameterIndex = 0; parameterIndex < methodArgumentsLength; parameterIndex++) {
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, parameterIndex);
|
||||
if (methodParameter.hasParameterAnnotation(Input.class)) {
|
||||
String inboundName = (String) AnnotationUtils.getValue(methodParameter.getParameterAnnotation(Input.class));
|
||||
String inboundName = (String) AnnotationUtils
|
||||
.getValue(methodParameter.getParameterAnnotation(Input.class));
|
||||
Assert.isTrue(StringUtils.hasText(inboundName), StreamListenerErrorMessages.INVALID_INBOUND_NAME);
|
||||
}
|
||||
if (methodParameter.hasParameterAnnotation(Output.class)) {
|
||||
String outboundName = (String) AnnotationUtils.getValue(methodParameter.getParameterAnnotation(Output.class));
|
||||
String outboundName = (String) AnnotationUtils
|
||||
.getValue(methodParameter.getParameterAnnotation(Output.class));
|
||||
Assert.isTrue(StringUtils.hasText(outboundName), StreamListenerErrorMessages.INVALID_OUTBOUND_NAME);
|
||||
}
|
||||
}
|
||||
if (methodArgumentsLength > 1){
|
||||
Assert.isTrue(inputAnnotationCount + outputAnnotationCount == methodArgumentsLength, StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
if (methodArgumentsLength > 1) {
|
||||
Assert.isTrue(inputAnnotationCount + outputAnnotationCount == methodArgumentsLength,
|
||||
StreamListenerErrorMessages.INVALID_DECLARATIVE_METHOD_PARAMETERS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void assertStreamListenerMessageHandlerMethod(Method method) {
|
||||
protected static void validateStreamListenerMessageHandler(Method method) {
|
||||
int methodArgumentsLength = method.getParameterTypes().length;
|
||||
if (methodArgumentsLength > 1) {
|
||||
int numAnnotatedMethodParameters = 0;
|
||||
@@ -114,7 +124,7 @@ public class StreamListenerMethodUtils {
|
||||
}
|
||||
}
|
||||
|
||||
protected static String getOutboundElementNameFromMethod(Method method) {
|
||||
protected static String getOutboundBindingTargetName(Method method) {
|
||||
SendTo sendTo = AnnotationUtils.findAnnotation(method, SendTo.class);
|
||||
if (sendTo != null) {
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(sendTo.value()), StreamListenerErrorMessages.ATLEAST_ONE_OUTPUT);
|
||||
|
||||
@@ -21,9 +21,8 @@ import org.springframework.core.MethodParameter;
|
||||
/**
|
||||
* Strategy for adapting a method argument type annotated with
|
||||
* {@link org.springframework.cloud.stream.annotation.Input} or
|
||||
* {@link org.springframework.cloud.stream.annotation.Output} from a bound element
|
||||
* (e.g. {@link org.springframework.messaging.MessageChannel}) supported by an
|
||||
* existing binder.
|
||||
* {@link org.springframework.cloud.stream.annotation.Output} from a binding type (e.g.
|
||||
* {@link org.springframework.messaging.MessageChannel}) supported by an existing binder.
|
||||
*
|
||||
* This is a framework extension and is not primarily intended for use by end-users.
|
||||
* @author Marius Bogoevici
|
||||
@@ -31,21 +30,22 @@ import org.springframework.core.MethodParameter;
|
||||
public interface StreamListenerParameterAdapter<A, B> {
|
||||
|
||||
/**
|
||||
* Return true if the conversion from the bound element type to the argument type
|
||||
* is supported.
|
||||
* @param boundElementType the bound element type
|
||||
* @param methodParameter the method parameter for which the conversion is performed
|
||||
* Return true if the conversion from the binding target type to the argument type is
|
||||
* supported.
|
||||
* @param bindingTargetType the binding target type
|
||||
* @param methodParameter the method parameter for which the conversion is performed
|
||||
* @return true if the conversion is supported
|
||||
*/
|
||||
boolean supports(Class<?> boundElementType, MethodParameter methodParameter);
|
||||
boolean supports(Class<?> bindingTargetType, MethodParameter methodParameter);
|
||||
|
||||
/**
|
||||
* Adapts the bound element to the argument type. The result will be passed
|
||||
* as argument to a method annotated with {@link org.springframework.cloud.stream.annotation.StreamListener}
|
||||
* when used for setting up a pipeline.
|
||||
* @param boundElement the bound element
|
||||
* @param parameter the method parameter for which the conversion is performed
|
||||
* Adapts the binding target to the argument type. The result will be passed as
|
||||
* argument to a method annotated with
|
||||
* {@link org.springframework.cloud.stream.annotation.StreamListener} when used for
|
||||
* setting up a pipeline.
|
||||
* @param bindingTarget the binding target
|
||||
* @param parameter the method parameter for which the conversion is performed
|
||||
* @return an instance of the parameter type, which will be passed to the method
|
||||
*/
|
||||
A adapt(B boundElement, MethodParameter parameter);
|
||||
A adapt(B bindingTarget, MethodParameter parameter);
|
||||
}
|
||||
|
||||
@@ -17,28 +17,30 @@
|
||||
package org.springframework.cloud.stream.binding;
|
||||
|
||||
/**
|
||||
* A strategy for adapting the result of a {@link org.springframework.cloud.stream.annotation.StreamListener}
|
||||
* annotated method to a bound element annotated with {@link org.springframework.cloud.stream.annotation.Output}.
|
||||
* A strategy for adapting the result of a
|
||||
* {@link org.springframework.cloud.stream.annotation.StreamListener} annotated method to
|
||||
* a binding target annotated with
|
||||
* {@link org.springframework.cloud.stream.annotation.Output}.
|
||||
*
|
||||
* Used when the {@link org.springframework.cloud.stream.annotation.StreamListener} annotated method is operating in
|
||||
* declarative mode.
|
||||
* Used when the {@link org.springframework.cloud.stream.annotation.StreamListener}
|
||||
* annotated method is operating in declarative mode.
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface StreamListenerResultAdapter<R, B> {
|
||||
|
||||
/**
|
||||
* Return true if the result type can be converted to the bound element.
|
||||
* @param resultType the result type.
|
||||
* @param boundElement the bound element.
|
||||
* Return true if the result type can be converted to the binding target.
|
||||
* @param resultType the result type.
|
||||
* @param bindingTarget the binding target.
|
||||
* @return true if the conversion can take place.
|
||||
*/
|
||||
boolean supports(Class<?> resultType, Class<?> boundElement);
|
||||
boolean supports(Class<?> resultType, Class<?> bindingTarget);
|
||||
|
||||
/**
|
||||
* Adapts the result to the bound element.
|
||||
* Adapts the result to the binding target.
|
||||
* @param streamListenerResult the result of invoking the method.
|
||||
* @param boundElement the bound element
|
||||
* @param bindingTarget the binding target.
|
||||
*/
|
||||
void adapt(R streamListenerResult, B boundElement);
|
||||
void adapt(R streamListenerResult, B bindingTarget);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,29 +20,30 @@ import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
* Class that {@link BindableProxyFactory} uses to create and configure message channels.
|
||||
* An implementation of {@link BindingTargetFactory} for creating {@link SubscribableChannel}s.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author David Syer
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public class DefaultBindableChannelFactory implements BindableChannelFactory {
|
||||
public class SubscribableChannelBindingTargetFactory extends AbstractBindingTargetFactory<SubscribableChannel> {
|
||||
|
||||
private final MessageChannelConfigurer messageChannelConfigurer;
|
||||
|
||||
public DefaultBindableChannelFactory(MessageChannelConfigurer messageChannelConfigurer) {
|
||||
public SubscribableChannelBindingTargetFactory(MessageChannelConfigurer messageChannelConfigurer) {
|
||||
super(SubscribableChannel.class);
|
||||
this.messageChannelConfigurer = messageChannelConfigurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscribableChannel createInputChannel(String name) {
|
||||
public SubscribableChannel createInput(String name) {
|
||||
SubscribableChannel subscribableChannel = new DirectChannel();
|
||||
this.messageChannelConfigurer.configureInputChannel(subscribableChannel, name);
|
||||
return subscribableChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscribableChannel createOutputChannel(String name) {
|
||||
public SubscribableChannel createOutput(String name) {
|
||||
SubscribableChannel subscribableChannel = new DirectChannel();
|
||||
this.messageChannelConfigurer.configureOutputChannel(subscribableChannel, name);
|
||||
return subscribableChannel;
|
||||
@@ -61,10 +61,10 @@ public class BinderFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(BinderFactory.class)
|
||||
public BinderFactory<?> binderFactory(BinderTypeRegistry binderTypeRegistry,
|
||||
ChannelBindingServiceProperties channelBindingServiceProperties) {
|
||||
public BinderFactory binderFactory(BinderTypeRegistry binderTypeRegistry,
|
||||
BindingServiceProperties bindingServiceProperties) {
|
||||
Map<String, BinderConfiguration> binderConfigurations = new HashMap<>();
|
||||
Map<String, BinderProperties> declaredBinders = channelBindingServiceProperties.getBinders();
|
||||
Map<String, BinderProperties> declaredBinders = bindingServiceProperties.getBinders();
|
||||
boolean defaultCandidatesExist = false;
|
||||
Iterator<Map.Entry<String, BinderProperties>> binderPropertiesIterator = declaredBinders.entrySet().iterator();
|
||||
while (!defaultCandidatesExist && binderPropertiesIterator.hasNext()) {
|
||||
@@ -94,8 +94,8 @@ public class BinderFactoryConfiguration {
|
||||
new BinderConfiguration(entry.getValue(), new Properties(), true, true));
|
||||
}
|
||||
}
|
||||
DefaultBinderFactory<?> binderFactory = new DefaultBinderFactory<>(binderConfigurations);
|
||||
binderFactory.setDefaultBinder(channelBindingServiceProperties.getDefaultBinder());
|
||||
DefaultBinderFactory binderFactory = new DefaultBinderFactory(binderConfigurations);
|
||||
binderFactory.setDefaultBinder(bindingServiceProperties.getDefaultBinder());
|
||||
return binderFactory;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class BinderFactoryConfiguration {
|
||||
Map<String, BinderType> binderTypes = new HashMap<>();
|
||||
ClassLoader classLoader = configurableApplicationContext.getClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = ChannelBindingAutoConfiguration.class.getClassLoader();
|
||||
classLoader = BindingAutoConfiguration.class.getClassLoader();
|
||||
}
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/spring.binders");
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.stream.binding.Bindable;
|
||||
import org.springframework.cloud.stream.binding.ChannelBindingService;
|
||||
import org.springframework.cloud.stream.binding.BindingService;
|
||||
import org.springframework.cloud.stream.endpoint.ChannelsEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -43,10 +43,10 @@ import org.springframework.messaging.MessageChannel;
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(ChannelBindingService.class)
|
||||
@ConditionalOnBean(BindingService.class)
|
||||
@EnableConfigurationProperties(DefaultPollerProperties.class)
|
||||
@AutoConfigureBefore(EndpointAutoConfiguration.class)
|
||||
public class ChannelBindingAutoConfiguration {
|
||||
public class BindingAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private DefaultPollerProperties poller;
|
||||
@@ -61,7 +61,7 @@ public class ChannelBindingAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChannelsEndpoint channelsEndpoint(ChannelBindingServiceProperties properties) {
|
||||
public ChannelsEndpoint channelsEndpoint(BindingServiceProperties properties) {
|
||||
return new ChannelsEndpoint(this.adapters, properties);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ public class BindingBeansRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
ClassUtils.resolveClassName(metadata.getClassName(), null),
|
||||
EnableBinding.class);
|
||||
for (Class<?> type : collectClasses(attrs, metadata.getClassName())) {
|
||||
BindingBeanDefinitionRegistryUtils.registerChannelBeanDefinitions(type,
|
||||
BindingBeanDefinitionRegistryUtils.registerBindingTargetBeanDefinitions(type,
|
||||
type.getName(), registry);
|
||||
BindingBeanDefinitionRegistryUtils.registerChannelsQualifiedBeanDefinitions(
|
||||
BindingBeanDefinitionRegistryUtils.registerBindingTargetsQualifiedBeanDefinitions(
|
||||
ClassUtils.resolveClassName(metadata.getClassName(), null), type,
|
||||
registry);
|
||||
}
|
||||
|
||||
@@ -34,20 +34,20 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.stream.binder.BinderFactory;
|
||||
import org.springframework.cloud.stream.binding.BindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.AbstractBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareRouterBeanPostProcessor;
|
||||
import org.springframework.cloud.stream.binding.ChannelBindingService;
|
||||
import org.springframework.cloud.stream.binding.BindingService;
|
||||
import org.springframework.cloud.stream.binding.CompositeMessageChannelConfigurer;
|
||||
import org.springframework.cloud.stream.binding.ContextStartAfterRefreshListener;
|
||||
import org.springframework.cloud.stream.binding.DefaultBindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.DynamicDestinationsBindable;
|
||||
import org.springframework.cloud.stream.binding.InputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.MessageChannelConfigurer;
|
||||
import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
|
||||
import org.springframework.cloud.stream.binding.OutputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.SingleChannelBindable;
|
||||
import org.springframework.cloud.stream.binding.SingleBindingTargetBindable;
|
||||
import org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor;
|
||||
import org.springframework.cloud.stream.binding.SubscribableChannelBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -76,8 +76,8 @@ import org.springframework.util.CollectionUtils;
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ChannelBindingServiceProperties.class)
|
||||
public class ChannelBindingServiceConfiguration {
|
||||
@EnableConfigurationProperties(BindingServiceProperties.class)
|
||||
public class BindingServiceConfiguration {
|
||||
|
||||
private static final String ERROR_CHANNEL_NAME = "error";
|
||||
|
||||
@@ -92,25 +92,24 @@ public class ChannelBindingServiceConfiguration {
|
||||
|
||||
@Bean
|
||||
// This conditional is intentionally not in an autoconfig (usually a bad idea) because
|
||||
// it is used to detect a ChannelBindingService in the parent context (which we know
|
||||
// it is used to detect a BindingService in the parent context (which we know
|
||||
// already exists).
|
||||
@ConditionalOnMissingBean(ChannelBindingService.class)
|
||||
public ChannelBindingService bindingService(ChannelBindingServiceProperties channelBindingServiceProperties,
|
||||
BinderFactory<MessageChannel> binderFactory) {
|
||||
return new ChannelBindingService(channelBindingServiceProperties, binderFactory);
|
||||
@ConditionalOnMissingBean(BindingService.class)
|
||||
public BindingService bindingService(BindingServiceProperties bindingServiceProperties,
|
||||
BinderFactory binderFactory) {
|
||||
return new BindingService(bindingServiceProperties, binderFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageConverterConfigurer messageConverterConfigurer(
|
||||
ChannelBindingServiceProperties channelBindingServiceProperties,
|
||||
public MessageConverterConfigurer messageConverterConfigurer(BindingServiceProperties bindingServiceProperties,
|
||||
CompositeMessageConverterFactory compositeMessageConverterFactory) {
|
||||
return new MessageConverterConfigurer(channelBindingServiceProperties,
|
||||
compositeMessageConverterFactory);
|
||||
return new MessageConverterConfigurer(bindingServiceProperties, compositeMessageConverterFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BindableChannelFactory channelFactory(CompositeMessageChannelConfigurer compositeMessageChannelConfigurer) {
|
||||
return new DefaultBindableChannelFactory(compositeMessageChannelConfigurer);
|
||||
public SubscribableChannelBindingTargetFactory channelFactory(
|
||||
CompositeMessageChannelConfigurer compositeMessageChannelConfigurer) {
|
||||
return new SubscribableChannelBindingTargetFactory(compositeMessageChannelConfigurer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -140,17 +139,17 @@ public class ChannelBindingServiceConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BinderAwareChannelResolver binderAwareChannelResolver(ChannelBindingService channelBindingService,
|
||||
BindableChannelFactory bindableChannelFactory, DynamicDestinationsBindable dynamicDestinationsBindable) {
|
||||
return new BinderAwareChannelResolver(channelBindingService, bindableChannelFactory,
|
||||
dynamicDestinationsBindable);
|
||||
public BinderAwareChannelResolver binderAwareChannelResolver(BindingService bindingService,
|
||||
AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory,
|
||||
DynamicDestinationsBindable dynamicDestinationsBindable) {
|
||||
return new BinderAwareChannelResolver(bindingService, bindingTargetFactory, dynamicDestinationsBindable);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("spring.cloud.stream.bindings." + ERROR_CHANNEL_NAME + ".destination")
|
||||
public SingleChannelBindable errorChannelBindable(
|
||||
public SingleBindingTargetBindable<MessageChannel> errorChannelBindable(
|
||||
@Qualifier(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) PublishSubscribeChannel errorChannel) {
|
||||
return new SingleChannelBindable(ERROR_CHANNEL_NAME, errorChannel);
|
||||
return new SingleBindingTargetBindable<MessageChannel>(ERROR_CHANNEL_NAME, errorChannel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -171,8 +170,8 @@ public class ChannelBindingServiceConfiguration {
|
||||
public static MessageHandlerMethodFactory messageHandlerMethodFactory(
|
||||
CompositeMessageConverterFactory compositeMessageConverterFactory) {
|
||||
DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
|
||||
messageHandlerMethodFactory.setMessageConverter(
|
||||
compositeMessageConverterFactory.getMessageConverterForAllRegistered());
|
||||
messageHandlerMethodFactory
|
||||
.setMessageConverter(compositeMessageConverterFactory.getMessageConverterForAllRegistered());
|
||||
return messageHandlerMethodFactory;
|
||||
}
|
||||
|
||||
@@ -180,8 +179,14 @@ public class ChannelBindingServiceConfiguration {
|
||||
public static StreamListenerAnnotationBeanPostProcessor bindToAnnotationBeanPostProcessor(
|
||||
@Lazy BinderAwareChannelResolver binderAwareChannelResolver,
|
||||
@Lazy MessageHandlerMethodFactory messageHandlerMethodFactory) {
|
||||
return new StreamListenerAnnotationBeanPostProcessor(binderAwareChannelResolver,
|
||||
messageHandlerMethodFactory);
|
||||
return new StreamListenerAnnotationBeanPostProcessor(binderAwareChannelResolver, messageHandlerMethodFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
// provided for backwards compatibility scenarios
|
||||
public ChannelBindingServiceProperties channelBindingServiceProperties(
|
||||
BindingServiceProperties bindingServiceProperties) {
|
||||
return new ChannelBindingServiceProperties(bindingServiceProperties);
|
||||
}
|
||||
|
||||
// IMPORTANT: Nested class to avoid instantiating all of the above early
|
||||
@@ -194,22 +199,18 @@ public class ChannelBindingServiceConfiguration {
|
||||
public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(
|
||||
final ConfigurableListableBeanFactory beanFactory) {
|
||||
// IMPORTANT: Lazy delegate to avoid instantiating all of the above early
|
||||
return new BinderAwareRouterBeanPostProcessor(
|
||||
new DestinationResolver<MessageChannel>() {
|
||||
return new BinderAwareRouterBeanPostProcessor(new DestinationResolver<MessageChannel>() {
|
||||
|
||||
@Override
|
||||
public MessageChannel resolveDestination(String name)
|
||||
throws DestinationResolutionException {
|
||||
if (PostProcessorConfiguration.this.binderAwareChannelResolver == null) {
|
||||
PostProcessorConfiguration.this.binderAwareChannelResolver = BeanFactoryUtils
|
||||
.beanOfType(beanFactory,
|
||||
BinderAwareChannelResolver.class);
|
||||
}
|
||||
return PostProcessorConfiguration.this.binderAwareChannelResolver
|
||||
.resolveDestination(name);
|
||||
}
|
||||
@Override
|
||||
public MessageChannel resolveDestination(String name) throws DestinationResolutionException {
|
||||
if (PostProcessorConfiguration.this.binderAwareChannelResolver == null) {
|
||||
PostProcessorConfiguration.this.binderAwareChannelResolver = BeanFactoryUtils
|
||||
.beanOfType(beanFactory, BinderAwareChannelResolver.class);
|
||||
}
|
||||
return PostProcessorConfiguration.this.binderAwareChannelResolver.resolveDestination(name);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,8 +226,7 @@ public class ChannelBindingServiceConfiguration {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME.equals(beanName)) {
|
||||
IntegrationEvaluationContextFactoryBean factoryBean =
|
||||
(IntegrationEvaluationContextFactoryBean) bean;
|
||||
IntegrationEvaluationContextFactoryBean factoryBean = (IntegrationEvaluationContextFactoryBean) bean;
|
||||
Map<String, PropertyAccessor> factoryBeanAccessors = factoryBean.getPropertyAccessors();
|
||||
for (Map.Entry<String, PropertyAccessor> entry : accessors.entrySet()) {
|
||||
if (!factoryBeanAccessors.containsKey(entry.getKey())) {
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.stream.binder.ConsumerProperties;
|
||||
import org.springframework.cloud.stream.binder.ProducerProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Marius Bogoevici
|
||||
* @author Gary Russell
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.stream")
|
||||
@JsonInclude(Include.NON_DEFAULT)
|
||||
public class BindingServiceProperties
|
||||
implements ApplicationContextAware, InitializingBean {
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
@Value("${INSTANCE_INDEX:${CF_INSTANCE_INDEX:0}}")
|
||||
private int instanceIndex;
|
||||
|
||||
private int instanceCount = 1;
|
||||
|
||||
private Map<String, BindingProperties> bindings = new TreeMap<>(
|
||||
String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
private Map<String, BinderProperties> binders = new HashMap<>();
|
||||
|
||||
private String defaultBinder;
|
||||
|
||||
private String[] dynamicDestinations = new String[0];
|
||||
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
public Map<String, BindingProperties> getBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
public void setBindings(Map<String, BindingProperties> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
public Map<String, BinderProperties> getBinders() {
|
||||
return this.binders;
|
||||
}
|
||||
|
||||
public void setBinders(Map<String, BinderProperties> binders) {
|
||||
this.binders = binders;
|
||||
}
|
||||
|
||||
public String getDefaultBinder() {
|
||||
return this.defaultBinder;
|
||||
}
|
||||
|
||||
public void setDefaultBinder(String defaultBinder) {
|
||||
this.defaultBinder = defaultBinder;
|
||||
}
|
||||
|
||||
public int getInstanceIndex() {
|
||||
return this.instanceIndex;
|
||||
}
|
||||
|
||||
public void setInstanceIndex(int instanceIndex) {
|
||||
this.instanceIndex = instanceIndex;
|
||||
}
|
||||
|
||||
public int getInstanceCount() {
|
||||
return this.instanceCount;
|
||||
}
|
||||
|
||||
public void setInstanceCount(int instanceCount) {
|
||||
this.instanceCount = instanceCount;
|
||||
}
|
||||
|
||||
public String[] getDynamicDestinations() {
|
||||
return this.dynamicDestinations;
|
||||
}
|
||||
|
||||
public void setDynamicDestinations(String[] dynamicDestinations) {
|
||||
this.dynamicDestinations = dynamicDestinations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
|
||||
// override the bindings store with the environment-initializing version if in a
|
||||
// Spring context
|
||||
this.bindings = new EnvironmentEntryInitializingTreeMap<>(this.applicationContext,
|
||||
BindingProperties.class, "spring.cloud.stream.default",
|
||||
new TreeMap<String, BindingProperties>(String.CASE_INSENSITIVE_ORDER));
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.conversionService == null) {
|
||||
this.conversionService = this.applicationContext.getBean(
|
||||
IntegrationUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME,
|
||||
ConversionService.class);
|
||||
}
|
||||
}
|
||||
|
||||
public String getBinder(String bindingName) {
|
||||
return getBindingProperties(bindingName).getBinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return configuration properties as Map.
|
||||
* @return map of binding configuration properties.
|
||||
*/
|
||||
public Map<String, Object> asMapProperties() {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put("instanceIndex", String.valueOf(getInstanceIndex()));
|
||||
properties.put("instanceCount", String.valueOf(getInstanceCount()));
|
||||
properties.put("defaultBinder", getDefaultBinder());
|
||||
properties.put("dynamicDestinations", getDynamicDestinations());
|
||||
for (Map.Entry<String, BindingProperties> entry : this.bindings.entrySet()) {
|
||||
properties.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
for (Map.Entry<String, BinderProperties> entry : this.binders.entrySet()) {
|
||||
properties.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public ConsumerProperties getConsumerProperties(String inputBindingName) {
|
||||
Assert.notNull(inputBindingName, "The input binding name cannot be null");
|
||||
ConsumerProperties consumerProperties = getBindingProperties(inputBindingName)
|
||||
.getConsumer();
|
||||
if (consumerProperties == null) {
|
||||
consumerProperties = new ConsumerProperties();
|
||||
}
|
||||
// propagate instance count and instance index if not already set
|
||||
if (consumerProperties.getInstanceCount() < 0) {
|
||||
consumerProperties.setInstanceCount(this.instanceCount);
|
||||
}
|
||||
if (consumerProperties.getInstanceIndex() < 0) {
|
||||
consumerProperties.setInstanceIndex(this.instanceIndex);
|
||||
}
|
||||
return consumerProperties;
|
||||
}
|
||||
|
||||
public ProducerProperties getProducerProperties(String outputBindingName) {
|
||||
Assert.notNull(outputBindingName, "The output binding name cannot be null");
|
||||
ProducerProperties producerProperties = getBindingProperties(outputBindingName)
|
||||
.getProducer();
|
||||
if (producerProperties == null) {
|
||||
producerProperties = new ProducerProperties();
|
||||
}
|
||||
return producerProperties;
|
||||
}
|
||||
|
||||
public BindingProperties getBindingProperties(String bindingName) {
|
||||
BindingProperties bindingProperties = new BindingProperties();
|
||||
if (this.bindings.containsKey(bindingName)) {
|
||||
BeanUtils.copyProperties(this.bindings.get(bindingName), bindingProperties);
|
||||
}
|
||||
if (bindingProperties.getDestination() == null) {
|
||||
bindingProperties.setDestination(bindingName);
|
||||
}
|
||||
return bindingProperties;
|
||||
}
|
||||
|
||||
public String getGroup(String bindingName) {
|
||||
return getBindingProperties(bindingName).getGroup();
|
||||
}
|
||||
|
||||
public String getBindingDestination(String bindingName) {
|
||||
return getBindingProperties(bindingName).getDestination();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,187 +16,114 @@
|
||||
|
||||
package org.springframework.cloud.stream.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.stream.binder.ConsumerProperties;
|
||||
import org.springframework.cloud.stream.binder.ProducerProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* Provides a wrapper around {@link BindingServiceProperties} for backwards compatibility.
|
||||
* @author Marius Bogoevici
|
||||
* @author Gary Russell
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.stream")
|
||||
@JsonInclude(Include.NON_DEFAULT)
|
||||
public class ChannelBindingServiceProperties implements ApplicationContextAware, InitializingBean {
|
||||
@Deprecated
|
||||
public class ChannelBindingServiceProperties {
|
||||
|
||||
private ConversionService conversionService;
|
||||
private final BindingServiceProperties bindingServiceProperties;
|
||||
|
||||
@Value("${INSTANCE_INDEX:${CF_INSTANCE_INDEX:0}}")
|
||||
private int instanceIndex;
|
||||
|
||||
private int instanceCount = 1;
|
||||
|
||||
private Map<String, BindingProperties> bindings = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
private Map<String, BinderProperties> binders = new HashMap<>();
|
||||
|
||||
private String defaultBinder;
|
||||
|
||||
private String[] dynamicDestinations = new String[0];
|
||||
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
public ChannelBindingServiceProperties(
|
||||
BindingServiceProperties bindingServiceProperties) {
|
||||
this.bindingServiceProperties = bindingServiceProperties;
|
||||
}
|
||||
|
||||
public Map<String, BindingProperties> getBindings() {
|
||||
return this.bindings;
|
||||
return bindingServiceProperties.getBindings();
|
||||
}
|
||||
|
||||
public void setBindings(Map<String, BindingProperties> bindings) {
|
||||
this.bindings = bindings;
|
||||
bindingServiceProperties.setBindings(bindings);
|
||||
}
|
||||
|
||||
public Map<String, BinderProperties> getBinders() {
|
||||
return this.binders;
|
||||
return bindingServiceProperties.getBinders();
|
||||
}
|
||||
|
||||
public void setBinders(Map<String, BinderProperties> binders) {
|
||||
this.binders = binders;
|
||||
bindingServiceProperties.setBinders(binders);
|
||||
}
|
||||
|
||||
public String getDefaultBinder() {
|
||||
return this.defaultBinder;
|
||||
return bindingServiceProperties.getDefaultBinder();
|
||||
}
|
||||
|
||||
public void setDefaultBinder(String defaultBinder) {
|
||||
this.defaultBinder = defaultBinder;
|
||||
bindingServiceProperties.setDefaultBinder(defaultBinder);
|
||||
}
|
||||
|
||||
public int getInstanceIndex() {
|
||||
return this.instanceIndex;
|
||||
return bindingServiceProperties.getInstanceIndex();
|
||||
}
|
||||
|
||||
public void setInstanceIndex(int instanceIndex) {
|
||||
this.instanceIndex = instanceIndex;
|
||||
bindingServiceProperties.setInstanceIndex(instanceIndex);
|
||||
}
|
||||
|
||||
public int getInstanceCount() {
|
||||
return this.instanceCount;
|
||||
return bindingServiceProperties.getInstanceCount();
|
||||
}
|
||||
|
||||
public void setInstanceCount(int instanceCount) {
|
||||
this.instanceCount = instanceCount;
|
||||
bindingServiceProperties.setInstanceCount(instanceCount);
|
||||
}
|
||||
|
||||
public String[] getDynamicDestinations() {
|
||||
return this.dynamicDestinations;
|
||||
return bindingServiceProperties.getDynamicDestinations();
|
||||
}
|
||||
|
||||
public void setDynamicDestinations(String[] dynamicDestinations) {
|
||||
this.dynamicDestinations = dynamicDestinations;
|
||||
bindingServiceProperties.setDynamicDestinations(dynamicDestinations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
|
||||
// override the bindings store with the environment-initializing version if in a Spring context
|
||||
this.bindings = new EnvironmentEntryInitializingTreeMap<>(this.applicationContext, BindingProperties.class,
|
||||
"spring.cloud.stream.default", new TreeMap<String, BindingProperties>(String.CASE_INSENSITIVE_ORDER));
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
bindingServiceProperties.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
bindingServiceProperties.setConversionService(conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.conversionService == null) {
|
||||
this.conversionService = this.applicationContext.getBean(
|
||||
IntegrationUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
||||
}
|
||||
bindingServiceProperties.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public String getBinder(String channelName) {
|
||||
return getBindingProperties(channelName).getBinder();
|
||||
return bindingServiceProperties.getBinder(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return configuration properties as Map.
|
||||
* @return map of channel binding configuration properties.
|
||||
*/
|
||||
public Map<String, Object> asMapProperties() {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put("instanceIndex", String.valueOf(getInstanceIndex()));
|
||||
properties.put("instanceCount", String.valueOf(getInstanceCount()));
|
||||
properties.put("defaultBinder", getDefaultBinder());
|
||||
properties.put("dynamicDestinations", getDynamicDestinations());
|
||||
for (Map.Entry<String, BindingProperties> entry : this.bindings.entrySet()) {
|
||||
properties.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
for (Map.Entry<String, BinderProperties> entry : this.binders.entrySet()) {
|
||||
properties.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return properties;
|
||||
return bindingServiceProperties.asMapProperties();
|
||||
}
|
||||
|
||||
public ConsumerProperties getConsumerProperties(String inputChannelName) {
|
||||
Assert.notNull(inputChannelName, "The input channel name cannot be null");
|
||||
ConsumerProperties consumerProperties = getBindingProperties(inputChannelName).getConsumer();
|
||||
if (consumerProperties == null) {
|
||||
consumerProperties = new ConsumerProperties();
|
||||
}
|
||||
// propagate instance count and instance index if not already set
|
||||
if (consumerProperties.getInstanceCount() < 0) {
|
||||
consumerProperties.setInstanceCount(this.instanceCount);
|
||||
}
|
||||
if (consumerProperties.getInstanceIndex() < 0) {
|
||||
consumerProperties.setInstanceIndex(this.instanceIndex);
|
||||
}
|
||||
return consumerProperties;
|
||||
return bindingServiceProperties.getConsumerProperties(inputChannelName);
|
||||
}
|
||||
|
||||
public ProducerProperties getProducerProperties(String outputChannelName) {
|
||||
Assert.notNull(outputChannelName, "The output channel name cannot be null");
|
||||
ProducerProperties producerProperties = getBindingProperties(outputChannelName).getProducer();
|
||||
if (producerProperties == null) {
|
||||
producerProperties = new ProducerProperties();
|
||||
}
|
||||
return producerProperties;
|
||||
return bindingServiceProperties.getProducerProperties(outputChannelName);
|
||||
}
|
||||
|
||||
public BindingProperties getBindingProperties(String channelName) {
|
||||
BindingProperties bindingProperties = new BindingProperties();
|
||||
if (this.bindings.containsKey(channelName)) {
|
||||
BeanUtils.copyProperties(this.bindings.get(channelName), bindingProperties);
|
||||
}
|
||||
if (bindingProperties.getDestination() == null) {
|
||||
bindingProperties.setDestination(channelName);
|
||||
}
|
||||
return bindingProperties;
|
||||
return bindingServiceProperties.getBindingProperties(channelName);
|
||||
}
|
||||
|
||||
public String getGroup(String channelName) {
|
||||
return getBindingProperties(channelName).getGroup();
|
||||
return bindingServiceProperties.getGroup(channelName);
|
||||
}
|
||||
|
||||
public String getBindingDestination(String channelName) {
|
||||
return getBindingProperties(channelName).getDestination();
|
||||
return bindingServiceProperties.getBindingDestination(channelName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
|
||||
@@ -29,10 +29,11 @@ import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.cloud.stream.binding.Bindable;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
|
||||
/**
|
||||
* An {@link Endpoint} that has the binding information on all the {@link Bindable} message channels.
|
||||
* An {@link Endpoint} that has the binding information on all the {@link Bindable}
|
||||
* message channels.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Ilayaperumal Gopinathan
|
||||
@@ -41,9 +42,9 @@ public class ChannelsEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
||||
|
||||
private List<Bindable> adapters;
|
||||
|
||||
private ChannelBindingServiceProperties properties;
|
||||
private BindingServiceProperties properties;
|
||||
|
||||
public ChannelsEndpoint(List<Bindable> adapters, ChannelBindingServiceProperties properties) {
|
||||
public ChannelsEndpoint(List<Bindable> adapters, BindingServiceProperties properties) {
|
||||
super("channels");
|
||||
this.adapters = adapters;
|
||||
this.properties = properties;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.internal;
|
||||
|
||||
/**
|
||||
* Contains the names of properties for the internal use of Spring Cloud Stream.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public abstract class InternalPropertyNames {
|
||||
|
||||
public static final String SPRING_CLOUD_STREAM_INTERNAL_PREFIX = "spring.cloud.stream.internal";
|
||||
|
||||
public static final String NAMESPACE_PROPERTY_NAME = SPRING_CLOUD_STREAM_INTERNAL_PREFIX + ".namespace";
|
||||
|
||||
public static final String SELF_CONTAINED_APP_PROPERTY_NAME = SPRING_CLOUD_STREAM_INTERNAL_PREFIX
|
||||
+ ".selfContained";
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.reflection;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Internal utilities for handling generics.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public abstract class GenericsUtils {
|
||||
|
||||
/**
|
||||
* For a specific class that implements or extends a parametrized type
|
||||
* returns the parameter of that interface at a given position. For example,
|
||||
* for this class:
|
||||
* <pre>
|
||||
* {@code
|
||||
* class MessageChannelBinder implements Binder<MessageChannel, ?, ?>
|
||||
* }
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* getParameterType(MessageChannelBinder.class, Binder.class, 0);
|
||||
* }
|
||||
*
|
||||
* will return {@code Binder}
|
||||
*
|
||||
* @param evaluatedClass the evaluated class
|
||||
* @param interfaceClass the parametrized interface
|
||||
* @param position the position
|
||||
* @return the parameter type if any
|
||||
* @throws IllegalStateException if the evaluated class does not implement the interface or
|
||||
*/
|
||||
public static Class<?> getParameterType(Class<?> evaluatedClass, Class<?> interfaceClass, int position) {
|
||||
Class<?> bindableType = null;
|
||||
Assert.isTrue(interfaceClass.isInterface(), "'interfaceClass' must be an interface");
|
||||
if (!interfaceClass.isAssignableFrom(evaluatedClass)) {
|
||||
throw new IllegalStateException(evaluatedClass + " does not implement " + interfaceClass);
|
||||
}
|
||||
ResolvableType currentType = ResolvableType.forType(evaluatedClass);
|
||||
while (!Object.class.equals(currentType.getRawClass()) && bindableType == null) {
|
||||
ResolvableType[] interfaces = currentType.getInterfaces();
|
||||
ResolvableType resolvableType = null;
|
||||
for (ResolvableType interfaceType : interfaces) {
|
||||
if (interfaceClass.equals(interfaceType.getRawClass())) {
|
||||
resolvableType = interfaceType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (resolvableType == null) {
|
||||
currentType = currentType.getSuperType();
|
||||
}
|
||||
else {
|
||||
ResolvableType[] generics = resolvableType.getGenerics();
|
||||
ResolvableType generic = generics[position];
|
||||
Class<?> resolvedParameter = generic.resolve();
|
||||
if (resolvedParameter != null) {
|
||||
bindableType = resolvedParameter;
|
||||
}
|
||||
else {
|
||||
bindableType = Object.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bindableType == null) {
|
||||
throw new IllegalStateException("Cannot find parameter of " + evaluatedClass.getName() + " for "
|
||||
+ interfaceClass + " at position " + position);
|
||||
}
|
||||
return bindableType;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration:\
|
||||
org.springframework.cloud.stream.config.ChannelBindingAutoConfiguration
|
||||
org.springframework.cloud.stream.config.BindingAutoConfiguration
|
||||
|
||||
@@ -27,9 +27,10 @@ import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder;
|
||||
import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder.SourceConfigurer;
|
||||
import org.springframework.cloud.stream.aggregate.SharedBindingTargetRegistry;
|
||||
import org.springframework.cloud.stream.aggregate.SharedChannelRegistry;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.binding.BindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.BindingTargetFactory;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
@@ -66,10 +67,26 @@ public class AggregationTest {
|
||||
.from(TestSource.class)
|
||||
.to(TestProcessor.class)
|
||||
.run();
|
||||
SharedBindingTargetRegistry sharedBindingTargetRegistry = aggregatedApplicationContext
|
||||
.getBean(SharedBindingTargetRegistry.class);
|
||||
BindingTargetFactory channelFactory = aggregatedApplicationContext
|
||||
.getBean(BindingTargetFactory.class);
|
||||
assertThat(channelFactory).isNotNull();
|
||||
assertThat(sharedBindingTargetRegistry.getAll().keySet()).hasSize(2);
|
||||
aggregatedApplicationContext.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testModuleAggregationUsingSharedChannelRegistry() {
|
||||
// test backward compatibility
|
||||
aggregatedApplicationContext = new AggregateApplicationBuilder(
|
||||
MockBinderRegistryConfiguration.class, "--server.port=0")
|
||||
.from(TestSource.class).to(TestProcessor.class).run();
|
||||
SharedChannelRegistry sharedChannelRegistry = aggregatedApplicationContext
|
||||
.getBean(SharedChannelRegistry.class);
|
||||
BindableChannelFactory channelFactory = aggregatedApplicationContext
|
||||
.getBean(BindableChannelFactory.class);
|
||||
BindingTargetFactory channelFactory = aggregatedApplicationContext
|
||||
.getBean(BindingTargetFactory.class);
|
||||
assertThat(channelFactory).isNotNull();
|
||||
assertThat(sharedChannelRegistry.getAll().keySet()).hasSize(2);
|
||||
aggregatedApplicationContext.close();
|
||||
@@ -301,8 +318,8 @@ public class AggregationTest {
|
||||
.namespace("bar").run();
|
||||
SharedChannelRegistry sharedChannelRegistry = aggregatedApplicationContext
|
||||
.getBean(SharedChannelRegistry.class);
|
||||
BindableChannelFactory channelFactory = aggregatedApplicationContext
|
||||
.getBean(BindableChannelFactory.class);
|
||||
BindingTargetFactory channelFactory = aggregatedApplicationContext
|
||||
.getBean(BindingTargetFactory.class);
|
||||
Object fooOutput = sharedChannelRegistry.get("foo.output");
|
||||
assertThat(fooOutput).isNotNull();
|
||||
assertThat(fooOutput).isInstanceOf(MessageChannel.class);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -51,7 +52,7 @@ public class ArbitraryInterfaceWithBindingTargetsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testArbitraryInterfaceChannelsBound() {
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindConsumer(eq("someQueue.0"), anyString(), eq(this.fooChannels.foo()),
|
||||
Mockito.<ConsumerProperties>any());
|
||||
verify(binder).bindConsumer(eq("someQueue.1"), anyString(), eq(this.fooChannels.bar()),
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -50,7 +51,7 @@ public class ArbitraryInterfaceWithDefaultsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testArbitraryInterfaceChannelsBound() {
|
||||
final Binder binder = this.binderFactory.getBinder(null);
|
||||
final Binder binder = this.binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindConsumer(eq("foo"), anyString(), eq(this.fooChannels.foo()),
|
||||
Mockito.<ConsumerProperties>any());
|
||||
verify(binder).bindConsumer(eq("bar"), anyString(), eq(this.fooChannels.bar()),
|
||||
|
||||
@@ -31,16 +31,16 @@ import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binding.BindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.AbstractBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
|
||||
import org.springframework.cloud.stream.binding.ChannelBindingService;
|
||||
import org.springframework.cloud.stream.binding.DefaultBindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.BindingService;
|
||||
import org.springframework.cloud.stream.binding.DynamicDestinationsBindable;
|
||||
import org.springframework.cloud.stream.binding.InputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
|
||||
import org.springframework.cloud.stream.binding.OutputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.SubscribableChannelBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -75,9 +75,9 @@ public class BinderAwareChannelResolverTests {
|
||||
|
||||
protected volatile Binder<MessageChannel, ConsumerProperties, ProducerProperties> binder;
|
||||
|
||||
protected volatile BindableChannelFactory bindableChannelFactory;
|
||||
protected volatile AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory;
|
||||
|
||||
protected volatile ChannelBindingServiceProperties channelBindingServiceProperties;
|
||||
protected volatile BindingServiceProperties bindingServiceProperties;
|
||||
|
||||
protected volatile DynamicDestinationsBindable dynamicDestinationsBindable;
|
||||
|
||||
@@ -87,29 +87,29 @@ public class BinderAwareChannelResolverTests {
|
||||
public void setupContext() throws Exception {
|
||||
producerBindings = new ArrayList<>();
|
||||
this.binder = new TestBinder();
|
||||
BinderFactory binderFactory = new BinderFactory<MessageChannel>() {
|
||||
BinderFactory binderFactory = new BinderFactory() {
|
||||
|
||||
@Override
|
||||
public Binder<MessageChannel, ConsumerProperties, ProducerProperties> getBinder(String configurationName) {
|
||||
return binder;
|
||||
public <T> Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties> getBinder(String configurationName, Class<? extends T> bindableType) {
|
||||
return (Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties>) binder;
|
||||
}
|
||||
};
|
||||
this.channelBindingServiceProperties = new ChannelBindingServiceProperties();
|
||||
this.bindingServiceProperties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindings = new HashMap<>();
|
||||
BindingProperties bindingProperties = new BindingProperties();
|
||||
bindingProperties.setContentType("text/plain");
|
||||
bindings.put("foo", bindingProperties);
|
||||
this.channelBindingServiceProperties.setBindings(bindings);
|
||||
ChannelBindingService channelBindingService = new ChannelBindingService(channelBindingServiceProperties,
|
||||
this.bindingServiceProperties.setBindings(bindings);
|
||||
BindingService bindingService = new BindingService(bindingServiceProperties,
|
||||
binderFactory);
|
||||
MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
|
||||
this.channelBindingServiceProperties,
|
||||
this.bindingServiceProperties,
|
||||
new CompositeMessageConverterFactory());
|
||||
messageConverterConfigurer.setBeanFactory(Mockito.mock(ConfigurableListableBeanFactory.class));
|
||||
messageConverterConfigurer.afterPropertiesSet();
|
||||
this.bindableChannelFactory = new DefaultBindableChannelFactory(messageConverterConfigurer);
|
||||
this.bindingTargetFactory = new SubscribableChannelBindingTargetFactory(messageConverterConfigurer);
|
||||
dynamicDestinationsBindable = new DynamicDestinationsBindable();
|
||||
this.resolver = new BinderAwareChannelResolver(channelBindingService, this.bindableChannelFactory,
|
||||
this.resolver = new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
|
||||
dynamicDestinationsBindable);
|
||||
this.resolver.setBeanFactory(context.getBeanFactory());
|
||||
context.getBeanFactory().registerSingleton("channelResolver", this.resolver);
|
||||
@@ -117,7 +117,7 @@ public class BinderAwareChannelResolverTests {
|
||||
context.registerSingleton("other", DirectChannel.class);
|
||||
context.registerSingleton(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
|
||||
DefaultMessageBuilderFactory.class);
|
||||
context.getBeanFactory().registerSingleton("channelBindingService", channelBindingService);
|
||||
context.getBeanFactory().registerSingleton("bindingService", bindingService);
|
||||
context.registerSingleton("inputBindingLifecycle", InputBindingLifecycle.class);
|
||||
context.registerSingleton("outputBindingLifecycle", OutputBindingLifecycle.class);
|
||||
context.refresh();
|
||||
@@ -171,24 +171,24 @@ public class BinderAwareChannelResolverTests {
|
||||
BindingProperties genericProperties = new BindingProperties();
|
||||
genericProperties.setContentType("text/plain");
|
||||
bindings.put("foo", genericProperties);
|
||||
this.channelBindingServiceProperties.setBindings(bindings);
|
||||
this.bindingServiceProperties.setBindings(bindings);
|
||||
@SuppressWarnings("unchecked")
|
||||
Binder binder = mock(Binder.class);
|
||||
Binder binder2 = mock(Binder.class);
|
||||
BinderFactory<MessageChannel> mockBinderFactory = Mockito.mock(BinderFactory.class);
|
||||
BinderFactory mockBinderFactory = Mockito.mock(BinderFactory.class);
|
||||
Binding<MessageChannel> fooBinding = Mockito.mock(Binding.class);
|
||||
Binding<MessageChannel> barBinding = Mockito.mock(Binding.class);
|
||||
when(binder.bindProducer(
|
||||
matches("foo"), any(DirectChannel.class), any(ProducerProperties.class))).thenReturn(fooBinding);
|
||||
when(binder2.bindProducer(
|
||||
matches("bar"), any(DirectChannel.class), any(ProducerProperties.class))).thenReturn(barBinding);
|
||||
when(mockBinderFactory.getBinder(null)).thenReturn(binder);
|
||||
when(mockBinderFactory.getBinder("someTransport")).thenReturn(binder2);
|
||||
ChannelBindingService channelBindingService = new ChannelBindingService(channelBindingServiceProperties,
|
||||
when(mockBinderFactory.getBinder(null, DirectChannel.class)).thenReturn(binder);
|
||||
when(mockBinderFactory.getBinder("someTransport", DirectChannel.class)).thenReturn(binder2);
|
||||
BindingService bindingService = new BindingService(bindingServiceProperties,
|
||||
mockBinderFactory);
|
||||
@SuppressWarnings("unchecked")
|
||||
BinderAwareChannelResolver resolver =
|
||||
new BinderAwareChannelResolver(channelBindingService, this.bindableChannelFactory,
|
||||
new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
|
||||
new DynamicDestinationsBindable());
|
||||
BeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
resolver.setBeanFactory(beanFactory);
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -95,10 +96,10 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("binder1");
|
||||
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
|
||||
Binder defaultBinder = binderFactory.getBinder(null);
|
||||
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
assertThat(defaultBinder).isSameAs(binder1);
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("binder1");
|
||||
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
|
||||
}
|
||||
|
||||
@@ -122,10 +123,10 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("custom");
|
||||
Binder binder1 = binderFactory.getBinder("custom", MessageChannel.class);
|
||||
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
|
||||
|
||||
assertThat(binderFactory.getBinder(null)).isSameAs(binder1);
|
||||
assertThat(binderFactory.getBinder(null, MessageChannel.class)).isSameAs(binder1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,7 +139,7 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("custom");
|
||||
Binder binder1 = binderFactory.getBinder("custom", MessageChannel.class);
|
||||
assertThat(binder1).hasFieldOrPropertyWithValue("name", null);
|
||||
}
|
||||
|
||||
@@ -157,18 +158,18 @@ public class BinderFactoryConfigurationTests {
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
try {
|
||||
binderFactory.getBinder(null);
|
||||
binderFactory.getBinder(null, MessageChannel.class);
|
||||
fail();
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getMessage()).contains(
|
||||
"A default binder has been requested, but there is more than one binder available:");
|
||||
"A default binder has been requested, but there is more than one binder available");
|
||||
}
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("binder1");
|
||||
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
Binder binder2 = binderFactory.getBinder("binder2");
|
||||
Binder binder2 = binderFactory.getBinder("binder2", MessageChannel.class);
|
||||
assertThat(binder2).isInstanceOf(StubBinder2.class);
|
||||
}
|
||||
|
||||
@@ -190,15 +191,15 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder defaultBinder = binderFactory.getBinder(null);
|
||||
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
assertThat(defaultBinder).isInstanceOf(StubBinder1.class);
|
||||
assertThat(((StubBinder1) defaultBinder).getName()).isNullOrEmpty();
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("binder1");
|
||||
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
assertThat(binder1).isSameAs(defaultBinder);
|
||||
|
||||
Binder custom = binderFactory.getBinder("custom");
|
||||
Binder custom = binderFactory.getBinder("custom", MessageChannel.class);
|
||||
assertThat(custom).isInstanceOf(StubBinder1.class);
|
||||
assertThat(custom).isNotSameAs(defaultBinder);
|
||||
assertThat(((StubBinder1) custom).getName()).isEqualTo("foo");
|
||||
@@ -222,12 +223,12 @@ public class BinderFactoryConfigurationTests {
|
||||
|
||||
BinderFactory binderFactory = context.getBean(BinderFactory.class);
|
||||
|
||||
Binder binder1 = binderFactory.getBinder("binder1");
|
||||
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
Binder binder2 = binderFactory.getBinder("binder2");
|
||||
Binder binder2 = binderFactory.getBinder("binder2", MessageChannel.class);
|
||||
assertThat(binder2).isInstanceOf(StubBinder2.class);
|
||||
|
||||
Binder defaultBinder = binderFactory.getBinder(null);
|
||||
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
assertThat(defaultBinder).isSameAs(binder2);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ public class ErrorBindingTests {
|
||||
public void testErrorChannelNotBoundByDefault() {
|
||||
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(TestProcessor.class, "--server.port=0");
|
||||
BinderFactory<?> binderFactory = applicationContext.getBean(BinderFactory.class);
|
||||
BinderFactory binderFactory = applicationContext.getBean(BinderFactory.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
|
||||
Mockito.verify(binder).bindConsumer(eq("input"), isNull(String.class), any(MessageChannel.class), any(ConsumerProperties.class));
|
||||
Mockito.verify(binder).bindProducer(eq("output"), any(MessageChannel.class), any(ProducerProperties.class));
|
||||
@@ -59,10 +59,10 @@ public class ErrorBindingTests {
|
||||
|
||||
ConfigurableApplicationContext applicationContext =
|
||||
SpringApplication.run(TestProcessor.class, "--spring.cloud.stream.bindings.error.destination=foo", "--server.port=0");
|
||||
BinderFactory<?> binderFactory = applicationContext.getBean(BinderFactory.class);
|
||||
BinderFactory binderFactory = applicationContext.getBean(BinderFactory.class, MessageChannel.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
|
||||
MessageChannel errorChannel = applicationContext.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME,
|
||||
MessageChannel.class);
|
||||
|
||||
@@ -30,14 +30,14 @@ import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
|
||||
import org.springframework.cloud.stream.binding.ChannelBindingService;
|
||||
import org.springframework.cloud.stream.binding.DefaultBindableChannelFactory;
|
||||
import org.springframework.cloud.stream.binding.BindingService;
|
||||
import org.springframework.cloud.stream.binding.DynamicDestinationsBindable;
|
||||
import org.springframework.cloud.stream.binding.InputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
|
||||
import org.springframework.cloud.stream.binding.OutputBindingLifecycle;
|
||||
import org.springframework.cloud.stream.binding.SubscribableChannelBindingTargetFactory;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.support.DefaultMessageBuilderFactory;
|
||||
@@ -68,29 +68,29 @@ public class ExtendedPropertiesBinderAwareChannelResolverTests extends BinderAwa
|
||||
public void setupContext() throws Exception {
|
||||
producerBindings = new ArrayList<>();
|
||||
this.binder = new TestBinder();
|
||||
BinderFactory binderFactory = new BinderFactory<MessageChannel>() {
|
||||
BinderFactory binderFactory = new BinderFactory() {
|
||||
|
||||
@Override
|
||||
public ExtendedPropertiesBinder<MessageChannel, ExtendedConsumerProperties, ExtendedProducerProperties> getBinder(String configurationName) {
|
||||
return binder;
|
||||
public <T> Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties> getBinder(String configurationName, Class<? extends T> bindableType) {
|
||||
return (Binder<T, ? extends ConsumerProperties, ? extends ProducerProperties>) binder;
|
||||
}
|
||||
};
|
||||
this.channelBindingServiceProperties = new ChannelBindingServiceProperties();
|
||||
this.bindingServiceProperties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindings = new HashMap<String, BindingProperties>();
|
||||
BindingProperties bindingProperties = new BindingProperties();
|
||||
bindingProperties.setContentType("text/plain");
|
||||
bindings.put("foo", bindingProperties);
|
||||
this.channelBindingServiceProperties.setBindings(bindings);
|
||||
this.bindingServiceProperties.setBindings(bindings);
|
||||
MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
|
||||
this.channelBindingServiceProperties,
|
||||
this.bindingServiceProperties,
|
||||
new CompositeMessageConverterFactory());
|
||||
messageConverterConfigurer.setBeanFactory(Mockito.mock(ConfigurableListableBeanFactory.class));
|
||||
messageConverterConfigurer.afterPropertiesSet();
|
||||
this.bindableChannelFactory = new DefaultBindableChannelFactory(messageConverterConfigurer);
|
||||
this.bindingTargetFactory = new SubscribableChannelBindingTargetFactory(messageConverterConfigurer);
|
||||
dynamicDestinationsBindable = new DynamicDestinationsBindable();
|
||||
ChannelBindingService channelBindingService = new ChannelBindingService(channelBindingServiceProperties,
|
||||
BindingService bindingService = new BindingService(bindingServiceProperties,
|
||||
binderFactory);
|
||||
this.resolver = new BinderAwareChannelResolver(channelBindingService, this.bindableChannelFactory,
|
||||
this.resolver = new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
|
||||
dynamicDestinationsBindable);
|
||||
this.resolver.setBeanFactory(context.getBeanFactory());
|
||||
context.getBeanFactory().registerSingleton("channelResolver", this.resolver);
|
||||
@@ -98,7 +98,7 @@ public class ExtendedPropertiesBinderAwareChannelResolverTests extends BinderAwa
|
||||
context.registerSingleton("other", DirectChannel.class);
|
||||
context.registerSingleton(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
|
||||
DefaultMessageBuilderFactory.class);
|
||||
context.getBeanFactory().registerSingleton("channelBindingService", channelBindingService);
|
||||
context.getBeanFactory().registerSingleton("bindingService", bindingService);
|
||||
context.registerSingleton("inputBindingLifecycle", InputBindingLifecycle.class);
|
||||
context.registerSingleton("outputBindingLifecycle", OutputBindingLifecycle.class);
|
||||
context.refresh();
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -54,9 +55,9 @@ public class HealthIndicatorsConfigurationTests {
|
||||
public void healthIndicatorsCheck() throws Exception {
|
||||
ConfigurableApplicationContext context = createBinderTestContext(new String[] { "binder1", "binder2" },
|
||||
"spring.cloud.stream.defaultBinder:binder2");
|
||||
Binder binder1 = context.getBean(BinderFactory.class).getBinder("binder1");
|
||||
Binder binder1 = context.getBean(BinderFactory.class).getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
Binder binder2 = context.getBean(BinderFactory.class).getBinder("binder2");
|
||||
Binder binder2 = context.getBean(BinderFactory.class).getBinder("binder2", MessageChannel.class);
|
||||
assertThat(binder2).isInstanceOf(StubBinder2.class);
|
||||
CompositeHealthIndicator bindersHealthIndicator = context.getBean("bindersHealthIndicator",
|
||||
CompositeHealthIndicator.class);
|
||||
@@ -81,9 +82,9 @@ public class HealthIndicatorsConfigurationTests {
|
||||
"spring.cloud.stream.defaultBinder:binder2",
|
||||
"management.health.binders.enabled:false");
|
||||
|
||||
Binder binder1 = context.getBean(BinderFactory.class).getBinder("binder1");
|
||||
Binder binder1 = context.getBean(BinderFactory.class).getBinder("binder1", MessageChannel.class);
|
||||
assertThat(binder1).isInstanceOf(StubBinder1.class);
|
||||
Binder binder2 = context.getBean(BinderFactory.class).getBinder("binder2");
|
||||
Binder binder2 = context.getBean(BinderFactory.class).getBinder("binder2", MessageChannel.class);
|
||||
assertThat(binder2).isInstanceOf(StubBinder2.class);
|
||||
try {
|
||||
context.getBean("bindersHealthIndicator", CompositeHealthIndicator.class);
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -48,7 +49,7 @@ public class InputOutputBindingOrderTest {
|
||||
public void testInputOutputBindingOrder() {
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(TestSource.class, "--server.port=-1");
|
||||
@SuppressWarnings("rawtypes")
|
||||
Binder binder = applicationContext.getBean(BinderFactory.class).getBinder(null);
|
||||
Binder binder = applicationContext.getBean(BinderFactory.class).getBinder(null, MessageChannel.class);
|
||||
Processor processor = applicationContext.getBean(Processor.class);
|
||||
// input is bound after the context has been started
|
||||
verify(binder).bindConsumer(eq("input"), anyString(), eq(processor.input()), Mockito.<ConsumerProperties>any());
|
||||
@@ -84,7 +85,7 @@ public class InputOutputBindingOrderTest {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void start() {
|
||||
Binder binder = this.binderFactory.getBinder(null);
|
||||
Binder binder = this.binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindProducer(eq("output"), eq(this.processor.output()), Mockito.<ProducerProperties>any());
|
||||
// input was not bound yet
|
||||
verifyNoMoreInteractions(binder);
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -51,7 +52,7 @@ public class ProcessorBindingWithBindingTargetsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
final Binder binder = binderFactory.getBinder(null);
|
||||
final Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindConsumer(eq("testtock.0"), anyString(),
|
||||
eq(this.testProcessor.input()), Mockito.<ConsumerProperties>any());
|
||||
verify(binder).bindProducer(eq("testtock.1"), eq(this.testProcessor.output()),
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -50,7 +51,7 @@ public class ProcessorBindingsWithDefaultsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
Binder binder = this.binderFactory.getBinder(null);
|
||||
Binder binder = this.binderFactory.getBinder(null, MessageChannel.class);
|
||||
Mockito.verify(binder).bindConsumer(eq("input"), anyString(), eq(this.processor.input()),
|
||||
Mockito.<ConsumerProperties>any());
|
||||
Mockito.verify(binder).bindProducer(eq("output"), eq(this.processor.output()),
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -52,7 +53,7 @@ public class SinkBindingWithDefaultTargetsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindConsumer(eq("testtock"), anyString(), eq(this.testSink.input()),
|
||||
Mockito.<ConsumerProperties>any());
|
||||
verifyNoMoreInteractions(binder);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
@@ -51,7 +52,7 @@ public class SinkBindingWithDefaultsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindConsumer(eq("input"), anyString(), eq(this.testSink.input()),
|
||||
Mockito.<ConsumerProperties>any());
|
||||
verifyNoMoreInteractions(binder);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.eq;
|
||||
@@ -59,7 +60,7 @@ public class SourceBindingWithBindingTargetsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindProducer(eq("testtock"), eq(this.testSource.output()),
|
||||
Mockito.<ProducerProperties>any());
|
||||
verifyNoMoreInteractions(binder);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.mockito.Matchers.eq;
|
||||
@@ -50,7 +51,7 @@ public class SourceBindingWithDefaultsTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSourceOutputChannelBound() {
|
||||
Binder binder = binderFactory.getBinder(null);
|
||||
Binder binder = binderFactory.getBinder(null, MessageChannel.class);
|
||||
verify(binder).bindProducer(eq("output"), eq(this.testSource.output()), Mockito.<ProducerProperties>any());
|
||||
verifyNoMoreInteractions(binder);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,9 @@ import org.springframework.cloud.stream.binder.ConsumerProperties;
|
||||
import org.springframework.cloud.stream.binder.DefaultBinderFactory;
|
||||
import org.springframework.cloud.stream.binder.ProducerProperties;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
|
||||
import org.springframework.cloud.stream.reflection.GenericsUtils;
|
||||
import org.springframework.cloud.stream.utils.MockBinderConfiguration;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -64,23 +65,23 @@ import static org.mockito.Mockito.when;
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public class ChannelBindingServiceTests {
|
||||
public class BindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void testDefaultGroup() throws Exception {
|
||||
ChannelBindingServiceProperties properties = new ChannelBindingServiceProperties();
|
||||
BindingServiceProperties properties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindingProperties = new HashMap<>();
|
||||
BindingProperties props = new BindingProperties();
|
||||
props.setDestination("foo");
|
||||
final String inputChannelName = "input";
|
||||
bindingProperties.put(inputChannelName, props);
|
||||
properties.setBindings(bindingProperties);
|
||||
DefaultBinderFactory<MessageChannel> binderFactory =
|
||||
new DefaultBinderFactory<>(Collections.singletonMap("mock",
|
||||
DefaultBinderFactory binderFactory =
|
||||
new DefaultBinderFactory(Collections.singletonMap("mock",
|
||||
new BinderConfiguration(new BinderType("mock", new Class[]{MockBinderConfiguration.class}),
|
||||
new Properties(), true, true)));
|
||||
Binder binder = binderFactory.getBinder("mock");
|
||||
ChannelBindingService service = new ChannelBindingService(properties, binderFactory);
|
||||
Binder binder = binderFactory.getBinder("mock", MessageChannel.class);
|
||||
BindingService service = new BindingService(properties, binderFactory);
|
||||
MessageChannel inputChannel = new DirectChannel();
|
||||
@SuppressWarnings("unchecked")
|
||||
Binding<MessageChannel> mockBinding = Mockito.mock(Binding.class);
|
||||
@@ -100,7 +101,7 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void testMultipleConsumerBindings() throws Exception {
|
||||
ChannelBindingServiceProperties properties = new ChannelBindingServiceProperties();
|
||||
BindingServiceProperties properties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindingProperties = new HashMap<>();
|
||||
BindingProperties props = new BindingProperties();
|
||||
props.setDestination("foo,bar");
|
||||
@@ -109,12 +110,12 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
properties.setBindings(bindingProperties);
|
||||
|
||||
DefaultBinderFactory<MessageChannel> binderFactory = new DefaultBinderFactory<>(Collections.singletonMap("mock",
|
||||
DefaultBinderFactory binderFactory = new DefaultBinderFactory(Collections.singletonMap("mock",
|
||||
new BinderConfiguration(new BinderType("mock", new Class[] { MockBinderConfiguration.class }),
|
||||
new Properties(), true, true)));
|
||||
|
||||
Binder binder = binderFactory.getBinder("mock");
|
||||
ChannelBindingService service = new ChannelBindingService(properties,
|
||||
Binder binder = binderFactory.getBinder("mock", MessageChannel.class);
|
||||
BindingService service = new BindingService(properties,
|
||||
binderFactory);
|
||||
MessageChannel inputChannel = new DirectChannel();
|
||||
|
||||
@@ -153,7 +154,7 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void testExplicitGroup() throws Exception {
|
||||
ChannelBindingServiceProperties properties = new ChannelBindingServiceProperties();
|
||||
BindingServiceProperties properties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindingProperties = new HashMap<>();
|
||||
BindingProperties props = new BindingProperties();
|
||||
props.setDestination("foo");
|
||||
@@ -161,7 +162,7 @@ public class ChannelBindingServiceTests {
|
||||
final String inputChannelName = "input";
|
||||
bindingProperties.put(inputChannelName, props);
|
||||
properties.setBindings(bindingProperties);
|
||||
DefaultBinderFactory<MessageChannel> binderFactory = new DefaultBinderFactory<>(
|
||||
DefaultBinderFactory binderFactory = new DefaultBinderFactory(
|
||||
Collections
|
||||
.singletonMap("mock",
|
||||
new BinderConfiguration(
|
||||
@@ -169,8 +170,8 @@ public class ChannelBindingServiceTests {
|
||||
new Class[] {
|
||||
MockBinderConfiguration.class }),
|
||||
new Properties(), true, true)));
|
||||
Binder binder = binderFactory.getBinder("mock");
|
||||
ChannelBindingService service = new ChannelBindingService(properties,
|
||||
Binder binder = binderFactory.getBinder("mock", MessageChannel.class);
|
||||
BindingService service = new BindingService(properties,
|
||||
binderFactory);
|
||||
MessageChannel inputChannel = new DirectChannel();
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -192,8 +193,8 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void checkDynamicBinding() {
|
||||
ChannelBindingServiceProperties properties = new ChannelBindingServiceProperties();
|
||||
DefaultBinderFactory<MessageChannel> binderFactory = new DefaultBinderFactory<>(
|
||||
BindingServiceProperties properties = new BindingServiceProperties();
|
||||
DefaultBinderFactory binderFactory = new DefaultBinderFactory(
|
||||
Collections
|
||||
.singletonMap("mock",
|
||||
new BinderConfiguration(
|
||||
@@ -201,19 +202,20 @@ public class ChannelBindingServiceTests {
|
||||
new Class[] {
|
||||
MockBinderConfiguration.class }),
|
||||
new Properties(), true, true)));
|
||||
Binder binder = binderFactory.getBinder("mock");
|
||||
Binder binder = binderFactory.getBinder("mock", MessageChannel.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Binding<MessageChannel> mockBinding = Mockito.mock(Binding.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final AtomicReference<MessageChannel> dynamic = new AtomicReference<>();
|
||||
when(binder.bindProducer(matches("foo"), any(DirectChannel.class),
|
||||
any(ProducerProperties.class))).thenReturn(mockBinding);
|
||||
ChannelBindingService channelBindingService = new ChannelBindingService(properties, binderFactory);
|
||||
BindingService bindingService = new BindingService(properties, binderFactory);
|
||||
SubscribableChannelBindingTargetFactory bindableSubscribableChannelFactory = new SubscribableChannelBindingTargetFactory(
|
||||
new MessageConverterConfigurer(properties,
|
||||
new CompositeMessageConverterFactory()));
|
||||
BinderAwareChannelResolver resolver = new BinderAwareChannelResolver(
|
||||
channelBindingService,
|
||||
new DefaultBindableChannelFactory(new MessageConverterConfigurer(
|
||||
properties,
|
||||
new CompositeMessageConverterFactory())), new DynamicDestinationsBindable());
|
||||
bindingService, bindableSubscribableChannelFactory,
|
||||
new DynamicDestinationsBindable());
|
||||
ConfigurableListableBeanFactory beanFactory = mock(
|
||||
ConfigurableListableBeanFactory.class);
|
||||
when(beanFactory.getBean("foo", MessageChannel.class))
|
||||
@@ -257,7 +259,7 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void testProducerPropertiesValidation() {
|
||||
ChannelBindingServiceProperties serviceProperties = new ChannelBindingServiceProperties();
|
||||
BindingServiceProperties serviceProperties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindingProperties = new HashMap<>();
|
||||
BindingProperties props = new BindingProperties();
|
||||
ProducerProperties producerProperties = new ProducerProperties();
|
||||
@@ -267,11 +269,11 @@ public class ChannelBindingServiceTests {
|
||||
final String outputChannelName = "output";
|
||||
bindingProperties.put(outputChannelName, props);
|
||||
serviceProperties.setBindings(bindingProperties);
|
||||
DefaultBinderFactory<MessageChannel> binderFactory =
|
||||
new DefaultBinderFactory<>(Collections.singletonMap("mock",
|
||||
DefaultBinderFactory binderFactory =
|
||||
new DefaultBinderFactory(Collections.singletonMap("mock",
|
||||
new BinderConfiguration(new BinderType("mock", new Class[]{MockBinderConfiguration.class}),
|
||||
new Properties(), true, true)));
|
||||
ChannelBindingService service = new ChannelBindingService(serviceProperties, binderFactory);
|
||||
BindingService service = new BindingService(serviceProperties, binderFactory);
|
||||
MessageChannel outputChannel = new DirectChannel();
|
||||
try {
|
||||
service.bindProducer(outputChannel, outputChannelName);
|
||||
@@ -284,7 +286,7 @@ public class ChannelBindingServiceTests {
|
||||
|
||||
@Test
|
||||
public void testConsumerPropertiesValidation() {
|
||||
ChannelBindingServiceProperties serviceProperties = new ChannelBindingServiceProperties();
|
||||
BindingServiceProperties serviceProperties = new BindingServiceProperties();
|
||||
Map<String, BindingProperties> bindingProperties = new HashMap<>();
|
||||
BindingProperties props = new BindingProperties();
|
||||
ConsumerProperties consumerProperties = new ConsumerProperties();
|
||||
@@ -294,10 +296,10 @@ public class ChannelBindingServiceTests {
|
||||
final String inputChannelName = "input";
|
||||
bindingProperties.put(inputChannelName, props);
|
||||
serviceProperties.setBindings(bindingProperties);
|
||||
DefaultBinderFactory<MessageChannel> binderFactory = new DefaultBinderFactory<>(Collections.singletonMap("mock",
|
||||
DefaultBinderFactory binderFactory = new DefaultBinderFactory(Collections.singletonMap("mock",
|
||||
new BinderConfiguration(new BinderType("mock", new Class[] { MockBinderConfiguration.class }),
|
||||
new Properties(), true, true)));
|
||||
ChannelBindingService service = new ChannelBindingService(serviceProperties,
|
||||
BindingService service = new BindingService(serviceProperties,
|
||||
binderFactory);
|
||||
MessageChannel inputChannel = new DirectChannel();
|
||||
try {
|
||||
@@ -308,4 +310,30 @@ public class ChannelBindingServiceTests {
|
||||
assertThat(e).hasMessageContaining("Concurrency should be greater than zero.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveBindableType() {
|
||||
Class<?> bindableType = GenericsUtils.getParameterType(FooBinder.class, Binder.class, 0);
|
||||
assertThat(bindableType).isSameAs(SomeBindableType.class);
|
||||
}
|
||||
|
||||
public static class FooBinder
|
||||
implements Binder<SomeBindableType, ConsumerProperties, ProducerProperties> {
|
||||
@Override
|
||||
public Binding<SomeBindableType> bindConsumer(String name, String group,
|
||||
SomeBindableType inboundBindTarget,
|
||||
ConsumerProperties consumerProperties) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Binding<SomeBindableType> bindProducer(String name,
|
||||
SomeBindableType outboundBindTarget,
|
||||
ProducerProperties producerProperties) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SomeBindableType {
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
@@ -62,7 +63,7 @@ public class PartitionedConsumerTest {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testBindingPartitionedConsumer() {
|
||||
Binder binder = this.binderFactory.getBinder(null);
|
||||
Binder binder = this.binderFactory.getBinder(null, MessageChannel.class);
|
||||
ArgumentCaptor<ConsumerProperties> argumentCaptor = ArgumentCaptor.forClass(ConsumerProperties.class);
|
||||
verify(binder).bindConsumer(eq("partIn"), anyString(), eq(this.testSink.input()),
|
||||
argumentCaptor.capture());
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
@@ -59,7 +60,7 @@ public class PartitionedProducerTest {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testBindingPartitionedProducer() {
|
||||
Binder binder = this.binderFactory.getBinder(null);
|
||||
Binder binder = this.binderFactory.getBinder(null, MessageChannel.class);
|
||||
ArgumentCaptor<ProducerProperties> argumentCaptor = ArgumentCaptor.forClass(ProducerProperties.class);
|
||||
verify(binder).bindProducer(eq("partOut"), eq(this.testSource.output()), argumentCaptor.capture());
|
||||
Assert.assertThat(argumentCaptor.getValue().getPartitionCount(), equalTo(3));
|
||||
|
||||
Reference in New Issue
Block a user