diff --git a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binding/MessageConverterConfigurer.java b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binding/MessageConverterConfigurer.java
index f6ac63313..94b7291ce 100644
--- a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binding/MessageConverterConfigurer.java
+++ b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binding/MessageConverterConfigurer.java
@@ -225,7 +225,9 @@ public class MessageConverterConfigurer implements MessageChannelConfigurer, Bea
@Override
public Message> preSend(Message> message, MessageChannel channel) {
Message> sentMessage = null;
- if (this.klazz.isAssignableFrom(message.getPayload().getClass())) {
+ if (this.klazz.isAssignableFrom(message.getPayload().getClass()) ||
+ (this.klazz.isAssignableFrom(String.class) && message.getPayload() instanceof byte[]
+ && !message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE) && !this.contentType.equals("text/plain"))) {
Object contentTypeFromMessage = message.getHeaders().get(MessageHeaders.CONTENT_TYPE);
if (contentTypeFromMessage == null) {
sentMessage = MessageConverterConfigurer.this.messageBuilderFactory
diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/contenttype/BinderConversionTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/contenttype/BinderConversionTests.java
new file mode 100644
index 000000000..582eb07dc
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/contenttype/BinderConversionTests.java
@@ -0,0 +1,80 @@
+/*
+ * 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.contenttype;
+
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Test;
+
+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.binder.integration.SourceDestination;
+import org.springframework.cloud.stream.binder.integration.SpringIntegrationBinderConfiguration;
+import org.springframework.cloud.stream.binder.integration.TargetDestination;
+import org.springframework.cloud.stream.messaging.Processor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Import;
+import org.springframework.messaging.handler.annotation.SendTo;
+import org.springframework.messaging.support.GenericMessage;
+
+/**
+ * Sort of a TCK test suite to validate payload conversion is
+ * done properly by interacting with binder's input/output destinations
+ * instead of its bridged channels.
+ * This means that all payloads (sent/received) must be expressed in the
+ * wire format (byte[])
+ *
+ * @author Oleg Zhurakousky
+ *
+ */
+public class BinderConversionTests {
+ @Test
+ public void test() {
+ ApplicationContext context = new SpringApplicationBuilder(ApplicationJsonDefaultType.class).web(false)
+ .run("--spring.cloud.stream.default.contentType=application/json", "--spring.jmx.enabled=false");
+ SourceDestination source = context.getBean(SourceDestination.class);
+ TargetDestination target = context.getBean(TargetDestination.class);
+ String jsonPayload = "{\"name\":\"oleg\"}";
+ source.send(new GenericMessage(jsonPayload.getBytes()));
+ System.out.println(new String((byte[])target.receive().getPayload(), StandardCharsets.UTF_8));
+ }
+
+ @SpringBootApplication
+ @EnableBinding(Processor.class)
+ @Import(SpringIntegrationBinderConfiguration.class)
+ public static class ApplicationJsonDefaultType {
+ @StreamListener(Processor.INPUT)
+ @SendTo(Processor.OUTPUT)
+ public Person echo(Person value) {
+ return value;
+ }
+
+ public static class Person {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+ }
+}
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..d3fabe8fc
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SampleStreamApp.java
@@ -0,0 +1,70 @@
+/*
+ * 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.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(false)
+ .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));
+ System.out.println();
+ }
+
+ @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..fdd190535
--- /dev/null
+++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/integration/SpringIntegrationChannelBinder.java
@@ -0,0 +1,267 @@
+/*
+ * 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.
+ *