/* * Copyright 2013-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.stream.binder; import java.io.IOException; import java.util.Collections; import java.util.List; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Registration; import org.junit.Before; import org.junit.Test; import org.springframework.cloud.stream.binder.AbstractBinder.JavaClassMimeTypeConversion; import org.springframework.integration.codec.kryo.KryoRegistrar; import org.springframework.integration.codec.kryo.PojoCodec; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.tuple.TupleKryoRegistrar; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.ContentTypeResolver; import org.springframework.messaging.support.GenericMessage; import org.springframework.tuple.DefaultTuple; import org.springframework.tuple.Tuple; import org.springframework.tuple.TupleBuilder; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import static org.assertj.core.api.Assertions.assertThat; /** * @author Gary Russell * @author David Turanski * @author Ilayaperumal Gopinathan */ public class MessageChannelBinderSupportTests { private final ContentTypeResolver contentTypeResolver = new StringConvertingContentTypeResolver(); private final TestMessageChannelBinder binder = new TestMessageChannelBinder(); @Before public void setUp() { binder.setCodec(new PojoCodec(new TupleRegistrar())); } @Test public void testBytesPassThru() { byte[] payload = "foo".getBytes(); Message<byte[]> message = MessageBuilder.withPayload(payload).build(); MessageValues converted = binder.serializePayloadIfNecessary(message); assertThat(converted.getPayload()).isSameAs(payload); Message<?> convertedMessage = converted.toMessage(); assertThat(convertedMessage.getPayload()).isSameAs(payload); assertThat(contentTypeResolver.resolve(convertedMessage.getHeaders())) .isEqualTo(MimeTypeUtils.APPLICATION_OCTET_STREAM); MessageValues reconstructed = binder.deserializePayloadIfNecessary(convertedMessage); payload = (byte[]) reconstructed.getPayload(); assertThat(converted.getPayload()).isSameAs(payload); assertThat(reconstructed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); } @Test public void testBytesPassThruContentType() { byte[] payload = "foo".getBytes(); Message<byte[]> message = MessageBuilder.withPayload(payload) .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE).build(); MessageValues messageValues = binder.serializePayloadIfNecessary(message); Message<?> converted = messageValues.toMessage(); assertThat(converted.getPayload()).isSameAs(payload); assertThat(contentTypeResolver.resolve(converted.getHeaders())) .isEqualTo(MimeTypeUtils.APPLICATION_OCTET_STREAM); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); payload = (byte[]) reconstructed.getPayload(); assertThat(converted.getPayload()).isSameAs(payload); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)) .isEqualTo(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE); assertThat(reconstructed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); } @Test public void testString() throws IOException { MessageValues convertedValues = binder.serializePayloadIfNecessary(new GenericMessage<>("foo")); Message<?> converted = convertedValues.toMessage(); assertThat(contentTypeResolver.resolve(converted.getHeaders())).isEqualTo(MimeTypeUtils.TEXT_PLAIN); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); assertThat(reconstructed.getPayload()).isEqualTo("foo"); assertThat(reconstructed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.TEXT_PLAIN_VALUE); } @Test public void testStringXML() throws IOException { Message<?> message = MessageBuilder .withPayload("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><test></test>") .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_XML).build(); Message<?> converted = binder.serializePayloadIfNecessary(message).toMessage(); assertThat(contentTypeResolver.resolve(converted.getHeaders())).isEqualTo(MimeTypeUtils.TEXT_PLAIN); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); assertThat(reconstructed.getPayload()) .isEqualTo("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><test></test>"); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.TEXT_XML.toString()); } @Test public void testContentTypePreservedForJson() throws IOException { Message<String> inbound = MessageBuilder.withPayload("{\"foo\":\"foo\"}") .copyHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)) .build(); MessageValues convertedValues = binder.serializePayloadIfNecessary(inbound); Message<?> converted = convertedValues.toMessage(); assertThat(contentTypeResolver.resolve(converted.getHeaders())).isEqualTo(MimeTypeUtils.APPLICATION_JSON); assertThat(converted.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); assertThat(reconstructed.getPayload()).isEqualTo("{\"foo\":\"foo\"}"); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.APPLICATION_JSON_VALUE); } @Test public void testContentTypePreservedForNonSCStApp() { Message<String> inbound = MessageBuilder.withPayload("{\"foo\":\"bar\"}") .copyHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)) .build(); MessageValues reconstructed = binder.deserializePayloadIfNecessary(inbound); assertThat(reconstructed.getPayload()).isEqualTo("{\"foo\":\"bar\"}"); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.APPLICATION_JSON); } @Test public void testPojoSerialization() { MessageValues convertedValues = binder.serializePayloadIfNecessary(new GenericMessage<>(new Foo("bar"))); Message<?> converted = convertedValues.toMessage(); MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders()); assertThat(mimeType.getType()).isEqualTo("application"); assertThat(mimeType.getSubtype()).isEqualTo("x-java-object"); assertThat(mimeType.getParameter("type")).isEqualTo(Foo.class.getName()); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); assertThat(((Foo) reconstructed.getPayload()).getBar()).isEqualTo("bar"); assertThat(reconstructed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)).isEqualTo( "application/x-java-object;type=org.springframework.cloud.stream.binder.MessageChannelBinderSupportTests$Foo"); } @Test public void testTupleSerialization() { Tuple payload = TupleBuilder.tuple().of("foo", "bar"); MessageValues convertedValues = binder.serializePayloadIfNecessary(new GenericMessage<>(payload)); Message<?> converted = convertedValues.toMessage(); MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders()); assertThat(mimeType.getType()).isEqualTo("application"); assertThat(mimeType.getSubtype()).isEqualTo("x-java-object"); assertThat(mimeType.getParameter("type")).isEqualTo(DefaultTuple.class.getName()); MessageValues reconstructed = binder.deserializePayloadIfNecessary(converted); assertThat(((Tuple) reconstructed.getPayload()).getString("foo")).isEqualTo("bar"); assertThat(reconstructed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull(); assertThat(reconstructed.get(MessageHeaders.CONTENT_TYPE)) .isEqualTo("application/x-java-object;type=org.springframework.tuple.DefaultTuple"); } @Test public void mimeTypeIsSimpleObject() throws ClassNotFoundException { MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new Object(), null); String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt); assertThat(Class.forName(className)).isEqualTo(Object.class); } @Test public void mimeTypeIsObjectArray() throws ClassNotFoundException { MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new String[0], null); String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt); assertThat(Class.forName(className)).isEqualTo(String[].class); } @Test public void mimeTypeIsMultiDimensionalObjectArray() throws ClassNotFoundException { MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new String[0][0][0], null); String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt); assertThat(Class.forName(className)).isEqualTo(String[][][].class); } @Test public void mimeTypeIsPrimitiveArray() throws ClassNotFoundException { MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new int[0], null); String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt); assertThat(Class.forName(className)).isEqualTo(int[].class); } @Test public void mimeTypeIsMultiDimensionalPrimitiveArray() throws ClassNotFoundException { MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new int[0][0][0], null); String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt); assertThat(Class.forName(className)).isEqualTo(int[][][].class); } public static class Foo { private String bar; public Foo() { } public Foo(String bar) { this.bar = bar; } public String getBar() { return bar; } public void setBar(String bar) { this.bar = bar; } } public static class Bar { private String foo; public Bar() { } public Bar(String foo) { this.foo = foo; } public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } } public class TestMessageChannelBinder extends AbstractBinder<MessageChannel, ConsumerProperties, ProducerProperties> { @Override protected Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel channel, ConsumerProperties properties) { return null; } @Override public Binding<MessageChannel> doBindProducer(String name, MessageChannel channel, ProducerProperties properties) { return null; } } private static class TupleRegistrar implements KryoRegistrar { private final TupleKryoRegistrar delegate = new TupleKryoRegistrar(); @Override public void registerTypes(Kryo kryo) { delegate.registerTypes(kryo); } @Override public List<Registration> getRegistrations() { return delegate.getRegistrations(); } } }