diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderAwareChannelResolverTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderAwareChannelResolverTests.java
index 477fd44f4..99e4f308c 100644
--- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderAwareChannelResolverTests.java
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderAwareChannelResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2016 the original author or authors.
+ * Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -28,25 +27,19 @@ import org.junit.Before;
import org.junit.Test;
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.boot.WebApplicationType;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cloud.stream.binder.integration.SpringIntegrationBinderConfiguration;
import org.springframework.cloud.stream.binding.AbstractBindingTargetFactory;
+import org.springframework.cloud.stream.binding.Bindable;
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
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.BindingServiceProperties;
-import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
-import org.springframework.context.support.StaticApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.channel.DirectChannel;
-import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilder;
-import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@@ -54,10 +47,11 @@ import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.matches;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -66,10 +60,11 @@ import static org.mockito.Mockito.when;
* @author Mark Fisher
* @author Gary Russell
* @author Ilayaperumal Gopinathan
+ * @author Oleg Zhurakousky
*/
public class BinderAwareChannelResolverTests {
- protected final StaticApplicationContext context = new StaticApplicationContext();
+ protected ConfigurableApplicationContext context;
protected volatile BinderAwareChannelResolver resolver;
@@ -81,68 +76,43 @@ public class BinderAwareChannelResolverTests {
protected volatile DynamicDestinationsBindable dynamicDestinationsBindable;
- private volatile List producerBindings;
-
@Before
public void setupContext() throws Exception {
- producerBindings = new ArrayList<>();
- this.binder = new TestBinder();
- BinderFactory binderFactory = new BinderFactory() {
-
- @Override
- public Binder getBinder(
- String configurationName, Class extends T> bindableType) {
- return (Binder) binder;
- }
- };
- this.bindingServiceProperties = new BindingServiceProperties();
- Map bindings = new HashMap<>();
- BindingProperties bindingProperties = new BindingProperties();
- bindingProperties.setContentType("text/plain");
- bindings.put("foo", bindingProperties);
- this.bindingServiceProperties.setBindings(bindings);
- BindingService bindingService = new BindingService(bindingServiceProperties,
- binderFactory);
- MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
- this.bindingServiceProperties,
- new CompositeMessageConverterFactory());
- messageConverterConfigurer.setBeanFactory(Mockito.mock(ConfigurableListableBeanFactory.class));
- messageConverterConfigurer.afterPropertiesSet();
- this.bindingTargetFactory = new SubscribableChannelBindingTargetFactory(messageConverterConfigurer);
- dynamicDestinationsBindable = new DynamicDestinationsBindable();
- this.resolver = new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
- dynamicDestinationsBindable);
- this.resolver.setBeanFactory(context.getBeanFactory());
- context.getBeanFactory().registerSingleton("channelResolver", this.resolver);
- context.getBeanFactory().registerSingleton("dynamicDestinationBindable", this.dynamicDestinationsBindable);
- context.registerSingleton("other", DirectChannel.class);
- context.registerSingleton(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
- DefaultMessageBuilderFactory.class);
- context.getBeanFactory().registerSingleton("bindingService", bindingService);
- context.registerSingleton("inputBindingLifecycle", InputBindingLifecycle.class);
- context.registerSingleton("outputBindingLifecycle", OutputBindingLifecycle.class);
- context.refresh();
+ this.context = new SpringApplicationBuilder(SpringIntegrationBinderConfiguration.getCompleteConfiguration()).web(WebApplicationType.NONE).run();
+ this.resolver = context.getBean(BinderAwareChannelResolver.class);
+ this.binder = context.getBean(Binder.class);
+ this.bindingServiceProperties = context.getBean(BindingServiceProperties.class);
+ this.bindingTargetFactory = context.getBean(AbstractBindingTargetFactory.class);
}
@Test
public void resolveChannel() {
- assertThat(producerBindings).hasSize(0);
+ Map bindables = context.getBeansOfType(Bindable.class);
+ assertThat(bindables).hasSize(1);
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size()); // producer
+ assertEquals(0, bindable.getOutputs().size());// consumer
+ }
MessageChannel registered = resolver.resolveDestination("foo");
- assertThat(producerBindings).hasSize(1);
- TestBinder.TestBinding binding = producerBindings.get(0);
- assertThat(binding.isBound()).describedAs("Must be bound");
+
+ bindables = context.getBeansOfType(Bindable.class);
+ assertThat(bindables).hasSize(1);
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size()); // producer
+ assertEquals(1, bindable.getOutputs().size());// consumer
+ }
DirectChannel testChannel = new DirectChannel();
+ testChannel.setComponentName("INPUT");
final CountDownLatch latch = new CountDownLatch(1);
final List> received = new ArrayList<>();
testChannel.subscribe(new MessageHandler() {
-
@Override
public void handleMessage(Message> message) throws MessagingException {
received.add(message);
latch.countDown();
}
});
- binder.bindConsumer("foo", null, testChannel, new ConsumerProperties());
+ this.binder.bindConsumer("foo", null, testChannel, new ConsumerProperties());
assertThat(received).hasSize(0);
registered.send(MessageBuilder.withPayload("hello").build());
try {
@@ -154,15 +124,18 @@ public class BinderAwareChannelResolverTests {
}
assertThat(received).hasSize(1);
assertThat(new String((byte[])received.get(0).getPayload())).isEqualTo("hello");
- context.close();
- assertThat(producerBindings).hasSize(1);
- assertThat(binding.isBound()).isFalse().describedAs("Must not be bound");
+ this.context.close();
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size());
+ assertEquals(0, bindable.getOutputs().size());//Must not be bound"
+ }
}
@Test
public void resolveNonRegisteredChannel() {
MessageChannel other = resolver.resolveDestination("other");
assertThat(context.getBean("other")).isSameAs(other);
+ this.context.close();
}
@Test
@@ -173,7 +146,6 @@ public class BinderAwareChannelResolverTests {
genericProperties.setContentType("text/plain");
bindings.put("foo", genericProperties);
this.bindingServiceProperties.setBindings(bindings);
- @SuppressWarnings("unchecked")
Binder binder = mock(Binder.class);
Binder binder2 = mock(Binder.class);
BinderFactory mockBinderFactory = Mockito.mock(BinderFactory.class);
@@ -187,74 +159,12 @@ public class BinderAwareChannelResolverTests {
when(mockBinderFactory.getBinder("someTransport", DirectChannel.class)).thenReturn(binder2);
BindingService bindingService = new BindingService(bindingServiceProperties,
mockBinderFactory);
- @SuppressWarnings("unchecked")
BinderAwareChannelResolver resolver = new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
new DynamicDestinationsBindable());
- BeanFactory beanFactory = new DefaultListableBeanFactory();
- resolver.setBeanFactory(beanFactory);
+ resolver.setBeanFactory(context.getBeanFactory());
SubscribableChannel resolved = (SubscribableChannel) resolver.resolveDestination("foo");
verify(binder).bindProducer(eq("foo"), any(MessageChannel.class), any(ProducerProperties.class));
- assertThat(resolved).isSameAs(beanFactory.getBean("foo"));
- }
-
- /**
- * A simple test binder that creates queues for the destinations. Ignores groups.
- */
- private class TestBinder implements Binder {
-
- private final Map destinations = new ConcurrentHashMap<>();
-
- @Override
- public Binding bindConsumer(String name, String group,
- MessageChannel inboundBindTarget, ConsumerProperties properties) {
- synchronized (destinations) {
- if (!destinations.containsKey(name)) {
- destinations.put(name, new DirectChannel());
- }
- }
- DirectHandler directHandler = new DirectHandler(inboundBindTarget);
- destinations.get(name).subscribe(directHandler);
- return new TestBinding(name, directHandler);
- }
-
- @Override
- public Binding bindProducer(String name,
- MessageChannel outboundBindTarget, ProducerProperties properties) {
- synchronized (destinations) {
- if (!destinations.containsKey(name)) {
- destinations.put(name, new DirectChannel());
- }
- }
- DirectHandler directHandler = new DirectHandler(destinations.get(name));
- // for test purposes we can assume it is a SubscribableChannel
- ((SubscribableChannel) outboundBindTarget).subscribe(directHandler);
- TestBinding binding = new TestBinding(name, directHandler);
- producerBindings.add(binding);
- return binding;
- }
-
- private final class TestBinding implements Binding {
-
- private final String name;
-
- private final DirectHandler directHandler;
-
- private boolean bound = true;
-
- private TestBinding(String name, DirectHandler directHandler) {
- this.name = name;
- this.directHandler = directHandler;
- }
-
- @Override
- public void unbind() {
- bound = false;
- destinations.get(name).unsubscribe(directHandler);
- }
-
- public boolean isBound() {
- return bound;
- }
- }
+ assertThat(resolved).isSameAs(context.getBean("foo"));
+ this.context.close();
}
}
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/ExtendedPropertiesBinderAwareChannelResolverTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/ExtendedPropertiesBinderAwareChannelResolverTests.java
index f04e17987..fad06ca66 100644
--- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/ExtendedPropertiesBinderAwareChannelResolverTests.java
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/ExtendedPropertiesBinderAwareChannelResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2016 the original author or authors.
+ * Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,102 +17,49 @@
package org.springframework.cloud.stream.binder;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
-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.BindingServiceProperties;
-import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
+import org.springframework.cloud.stream.binding.Bindable;
import org.springframework.integration.channel.DirectChannel;
-import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilder;
-import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
-import org.springframework.messaging.SubscribableChannel;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author Mark Fisher
* @author Gary Russell
* @author Ilayaperumal Gopinathan
+ * @author Oleg Zhurakousky
*/
public class ExtendedPropertiesBinderAwareChannelResolverTests extends BinderAwareChannelResolverTests {
- private volatile ExtendedPropertiesBinder binder;
-
- private volatile List producerBindings;
-
- @Before
- @Override
- public void setupContext() throws Exception {
- producerBindings = new ArrayList<>();
- this.binder = new TestBinder();
- BinderFactory binderFactory = new BinderFactory() {
-
- @Override
- public Binder getBinder(
- String configurationName, Class extends T> bindableType) {
- return (Binder) binder;
- }
- };
- this.bindingServiceProperties = new BindingServiceProperties();
- Map bindings = new HashMap();
- BindingProperties bindingProperties = new BindingProperties();
- bindingProperties.setContentType("text/plain");
- bindings.put("foo", bindingProperties);
- this.bindingServiceProperties.setBindings(bindings);
- MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
- this.bindingServiceProperties,
- new CompositeMessageConverterFactory());
- messageConverterConfigurer.setBeanFactory(Mockito.mock(ConfigurableListableBeanFactory.class));
- messageConverterConfigurer.afterPropertiesSet();
- this.bindingTargetFactory = new SubscribableChannelBindingTargetFactory(messageConverterConfigurer);
- dynamicDestinationsBindable = new DynamicDestinationsBindable();
- BindingService bindingService = new BindingService(bindingServiceProperties,
- binderFactory);
- this.resolver = new BinderAwareChannelResolver(bindingService, this.bindingTargetFactory,
- dynamicDestinationsBindable);
- this.resolver.setBeanFactory(context.getBeanFactory());
- context.getBeanFactory().registerSingleton("channelResolver", this.resolver);
- context.getBeanFactory().registerSingleton("dynamicDestinationBindable", this.dynamicDestinationsBindable);
- context.registerSingleton("other", DirectChannel.class);
- context.registerSingleton(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
- DefaultMessageBuilderFactory.class);
- context.getBeanFactory().registerSingleton("bindingService", bindingService);
- context.registerSingleton("inputBindingLifecycle", InputBindingLifecycle.class);
- context.registerSingleton("outputBindingLifecycle", OutputBindingLifecycle.class);
- context.refresh();
- }
-
@Test
@Override
public void resolveChannel() {
- assertThat(producerBindings).hasSize(0);
+ Map bindables = context.getBeansOfType(Bindable.class);
+ assertThat(bindables).hasSize(1);
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size()); // producer
+ assertEquals(0, bindable.getOutputs().size());// consumer
+ }
MessageChannel registered = resolver.resolveDestination("foo");
- assertThat(producerBindings).hasSize(1);
- TestBinder.TestBinding binding = producerBindings.get(0);
- assertThat(binding.isBound()).describedAs("Must be bound");
+ bindables = context.getBeansOfType(Bindable.class);
+ assertThat(bindables).hasSize(1);
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size()); // producer
+ assertEquals(1, bindable.getOutputs().size());// consumer
+ }
DirectChannel testChannel = new DirectChannel();
final CountDownLatch latch = new CountDownLatch(1);
final List> received = new ArrayList<>();
@@ -124,7 +71,7 @@ public class ExtendedPropertiesBinderAwareChannelResolverTests extends BinderAwa
latch.countDown();
}
});
- binder.bindConsumer("foo", null, testChannel, new ExtendedConsumerProperties(new ConsumerProperties()));
+ binder.bindConsumer("foo", null, testChannel, new ExtendedConsumerProperties(new ConsumerProperties()));
assertThat(received).hasSize(0);
registered.send(MessageBuilder.withPayload("hello").build());
try {
@@ -137,79 +84,9 @@ public class ExtendedPropertiesBinderAwareChannelResolverTests extends BinderAwa
assertThat(received).hasSize(1);
assertThat(new String((byte[])received.get(0).getPayload())).isEqualTo("hello");
context.close();
- assertThat(producerBindings).hasSize(1);
- assertThat(binding.isBound()).isFalse().describedAs("Must not be bound");
- }
-
- /**
- * A simple test binder that creates queues for the destinations. Ignores groups.
- */
- class TestBinder implements
- ExtendedPropertiesBinder {
-
- private final Map destinations = new ConcurrentHashMap<>();
-
- @Override
- public Binding bindConsumer(String name, String group,
- MessageChannel inboundBindTarget, ExtendedConsumerProperties properties) {
- synchronized (destinations) {
- if (!destinations.containsKey(name)) {
- destinations.put(name, new DirectChannel());
- }
- }
- DirectHandler directHandler = new DirectHandler(inboundBindTarget);
- destinations.get(name).subscribe(directHandler);
- return new TestBinding(name, directHandler);
- }
-
- @Override
- public Binding bindProducer(String name,
- MessageChannel outboundBindTarget, ExtendedProducerProperties properties) {
- synchronized (destinations) {
- if (!destinations.containsKey(name)) {
- destinations.put(name, new DirectChannel());
- }
- }
- DirectHandler directHandler = new DirectHandler(destinations.get(name));
- // for test purposes we can assume it is a SubscribableChannel
- ((SubscribableChannel) outboundBindTarget).subscribe(directHandler);
- TestBinding binding = new TestBinding(name, directHandler);
- producerBindings.add(binding);
- return binding;
- }
-
- @Override
- public ExtendedConsumerProperties getExtendedConsumerProperties(String channelName) {
- return new ExtendedConsumerProperties(new ConsumerProperties());
- }
-
- @Override
- public ExtendedProducerProperties getExtendedProducerProperties(String channelName) {
- return new ExtendedProducerProperties(new ProducerProperties());
- }
-
- private final class TestBinding implements Binding {
-
- private final String name;
-
- private final DirectHandler directHandler;
-
- private boolean bound = true;
-
- private TestBinding(String name, DirectHandler directHandler) {
- this.name = name;
- this.directHandler = directHandler;
- }
-
- @Override
- public void unbind() {
- bound = false;
- destinations.get(name).unsubscribe(directHandler);
- }
-
- public boolean isBound() {
- return bound;
- }
+ for (Bindable bindable : bindables.values()) {
+ assertEquals(0, bindable.getInputs().size());
+ assertEquals(0, bindable.getOutputs().size());//Must not be bound"
}
}
}
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/AbstractDestination.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/AbstractDestination.java
new file mode 100644
index 000000000..bbd5ab28d
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/AbstractDestination.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.integration;
+
+import org.springframework.messaging.SubscribableChannel;
+
+/**
+ * @author Oleg Zhurakousky
+ *
+ */
+abstract class AbstractDestination {
+
+ private SubscribableChannel channel;
+
+ SubscribableChannel getChannel() {
+ return channel;
+ }
+
+ void setChannel(SubscribableChannel channel) {
+ this.channel = channel;
+ this.afterChannelIsSet();
+ }
+
+ void afterChannelIsSet() {
+ // noop
+ }
+}
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SampleStreamApp.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SampleStreamApp.java
new file mode 100644
index 000000000..23bed3c72
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SampleStreamApp.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.integration;
+
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.boot.WebApplicationType;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cloud.stream.annotation.EnableBinding;
+import org.springframework.cloud.stream.annotation.StreamListener;
+import org.springframework.cloud.stream.messaging.Processor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Import;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.handler.annotation.SendTo;
+import org.springframework.messaging.support.GenericMessage;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Sample spring cloud stream application that demonstrates the usage of {@link SpringIntegrationChannelBinder}.
+ *
+ * @author Oleg Zhurakousky
+ *
+ */
+@SpringBootApplication
+@EnableBinding(Processor.class)
+@Import(SpringIntegrationBinderConfiguration.class)
+public class SampleStreamApp {
+
+ public static void main(String[] args) {
+ ApplicationContext context = new SpringApplicationBuilder(SampleStreamApp.class).web(WebApplicationType.NONE)
+ .run("--server.port=0");
+ SourceDestination source = context.getBean(SourceDestination.class);
+ TargetDestination target = context.getBean(TargetDestination.class);
+ source.send(new GenericMessage("Hello".getBytes()));
+
+ Message> message = target.receive();
+ assertEquals("Hello", new String((byte[])message.getPayload(), StandardCharsets.UTF_8));
+ }
+
+ @StreamListener(Processor.INPUT)
+ @SendTo(Processor.OUTPUT)
+ public String receive(String value) {
+ System.out.println("Handling payload: " + value);
+ return value;
+ }
+
+ @ServiceActivator(inputChannel="input.anonymous.errors")
+ public void error(String value) {
+ System.out.println("Handling ERROR payload: " + value);
+ }
+}
+
+
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SourceDestination.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SourceDestination.java
new file mode 100644
index 000000000..ee03161c4
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SourceDestination.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.integration;
+
+import org.springframework.messaging.Message;
+
+/**
+ * Implementation of binder endpoint that represents the source destination
+ * (e.g., destination from which messages will be received by Processor.INPUT).
+ *
+ * You can interact with it by calling {@link #send(Message)} operation.
+ *
+ * @author Oleg Zhurakousky
+ *
+ */
+public class SourceDestination extends AbstractDestination {
+
+ /**
+ * Allows the {@link Message} to be sent to a Binder to be delegated
+ * to binder's input destination (e.g., Processor.INPUT).
+ *
+ */
+ public void send(Message> message) {
+ this.getChannel().send(message);
+ }
+}
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationBinderConfiguration.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationBinderConfiguration.java
new file mode 100644
index 000000000..56cb544ec
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationBinderConfiguration.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.integration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.cloud.stream.annotation.EnableBinding;
+import org.springframework.cloud.stream.binder.Binder;
+import org.springframework.cloud.stream.binder.BinderType;
+import org.springframework.cloud.stream.binder.BinderTypeRegistry;
+import org.springframework.cloud.stream.binder.ConsumerProperties;
+import org.springframework.cloud.stream.binder.DefaultBinderTypeRegistry;
+import org.springframework.cloud.stream.binder.ProducerProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.integration.config.EnableIntegration;
+
+/**
+ * {@link Binder} configuration backed by Spring Integration.
+ *
+ * Please see {@link SpringIntegrationChannelBinder} for more details.
+ *
+ * @author Oleg Zhurakousky
+ *
+ * @see SpringIntegrationChannelBinder
+ */
+@Configuration
+@ConditionalOnMissingBean(Binder.class)
+@EnableIntegration
+public class SpringIntegrationBinderConfiguration {
+
+ public static final String NAME = "integration";
+
+ /**
+ * Utility operation to return an array of configuration classes
+ * defined in {@link EnableBinding} annotation.
+ * Typically used for tests that do not rely on creating an SCSt boot
+ * application annotated with {@link EnableBinding}, yet require
+ * full {@link Binder} configuration.
+ */
+ public static Class>[] getCompleteConfiguration() {
+ List> configClasses = new ArrayList<>();
+ configClasses.add(SpringIntegrationBinderConfiguration.class);
+ Import annotation = AnnotationUtils.getAnnotation(EnableBinding.class, Import.class);
+ Map annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation);
+ configClasses.addAll(Arrays.asList((Class>[])annotationAttributes.get("value")));
+ return configClasses.toArray(new Class>[] {});
+ }
+
+ @Bean
+ public BinderTypeRegistry binderTypeRegistry() {
+ BinderType binderType = new BinderType(NAME, new Class[] {SpringIntegrationBinderConfiguration.class});
+ BinderTypeRegistry btr = new DefaultBinderTypeRegistry(Collections.singletonMap(NAME, binderType));
+ return btr;
+ }
+
+ @Bean
+ public SourceDestination sourceDestination() {
+ return new SourceDestination();
+ }
+
+ @Bean
+ public TargetDestination targetDestination() {
+ return new TargetDestination();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Bean
+ public Binder springIntegrationChannelBinder(SpringIntegrationProvisioner provisioner) {
+ return (Binder) new SpringIntegrationChannelBinder(provisioner);
+ }
+
+ @Bean
+ public SpringIntegrationProvisioner springIntegrationProvisioner() {
+ return new SpringIntegrationProvisioner();
+ }
+
+}
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationChannelBinder.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationChannelBinder.java
new file mode 100644
index 000000000..aa1d8921b
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationChannelBinder.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2015-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.binder.integration;
+
+import java.util.function.Consumer;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.stream.binder.AbstractMessageChannelBinder;
+import org.springframework.cloud.stream.binder.Binder;
+import org.springframework.cloud.stream.binder.ConsumerProperties;
+import org.springframework.cloud.stream.binder.ProducerProperties;
+import org.springframework.cloud.stream.binder.integration.SpringIntegrationProvisioner.SpringIntegrationConsumerDestination;
+import org.springframework.cloud.stream.binder.integration.SpringIntegrationProvisioner.SpringIntegrationProducerDestination;
+import org.springframework.cloud.stream.provisioning.ConsumerDestination;
+import org.springframework.cloud.stream.provisioning.ProducerDestination;
+import org.springframework.core.AttributeAccessor;
+import org.springframework.integration.core.MessageProducer;
+import org.springframework.integration.endpoint.MessageProducerSupport;
+import org.springframework.integration.handler.BridgeHandler;
+import org.springframework.integration.support.DefaultErrorMessageStrategy;
+import org.springframework.integration.support.ErrorMessageStrategy;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.messaging.MessagingException;
+import org.springframework.messaging.SubscribableChannel;
+import org.springframework.retry.RecoveryCallback;
+import org.springframework.retry.RetryCallback;
+import org.springframework.retry.RetryContext;
+import org.springframework.retry.RetryListener;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Implementation of {@link Binder} backed by Spring Integration framework.
+ * It is useful for localized demos and testing.
+ *
+ * This binder extends from the same base class ({@link AbstractMessageChannelBinder}) as
+ * other binders (i.e., Rabbit, Kafka etc). Interaction with this binder is done via source
+ * and target destination which emulate real binder's destinations (i.e., Kafka topic)
+ *
+ * The destination classes are
+ *
+ *
{@link SourceDestination}
+ *
{@link TargetDestination}
+ *
+ * Simply autowire them in your your application and send/receive messages.
+ *
+ * You must also add {@link SpringIntegrationBinderConfiguration} to your configuration.
+ * Below is the example using Spring Boot test.
+ *