From 31ce03a0d72531ef9018948360c168309bf3caf7 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 20 Jun 2017 14:46:46 -0400 Subject: [PATCH] GH-993: Conversion Interceptor Improvements Fixes #993 If the converter cannot convert due to a lingering content-type header, take a second shot with the header removed. If we still fail, throw a `MessageConversionException`. --- .../binding/MessageConverterConfigurer.java | 51 ++++++-- .../MessageConverterConfigurerTests.java | 111 ++++++++++++++++++ 2 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binding/MessageConverterConfigurerTests.java 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 88336823c..f6ac63313 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 @@ -243,34 +243,59 @@ public class MessageConverterConfigurer implements MessageChannelConfigurer, Bea if (this.provideHint) { converted = ((AbstractMessageConverter) this.messageConverter).fromMessage(message, this.klazz, this.mimeType); + if (converted == null && message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + converted = ((AbstractMessageConverter) this.messageConverter).fromMessage( + MessageConverterConfigurer.this.messageBuilderFactory.fromMessage(message) + .removeHeader(MessageHeaders.CONTENT_TYPE) + .build(), this.klazz, this.mimeType); + } } else { converted = this.messageConverter.fromMessage(message, this.klazz); + if (converted == null && message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + converted = this.messageConverter.fromMessage( + MessageConverterConfigurer.this.messageBuilderFactory.fromMessage(message) + .removeHeader(MessageHeaders.CONTENT_TYPE) + .build(), this.klazz); + } } } else { + MutableMessageHeaders headers = new MutableMessageHeaders(message.getHeaders()); if (this.provideHint) { converted = ((AbstractMessageConverter) this.messageConverter).toMessage(message.getPayload(), - new MutableMessageHeaders(message.getHeaders()), this.mimeType); + headers, this.mimeType); + if (converted == null && message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + headers.remove(MessageHeaders.CONTENT_TYPE); + converted = ((AbstractMessageConverter) this.messageConverter).toMessage(message.getPayload(), + headers, this.mimeType); + } } else { - converted = this.messageConverter.toMessage(message.getPayload(), - new MutableMessageHeaders(message.getHeaders())); + converted = this.messageConverter.toMessage(message.getPayload(), headers); + if (converted == null && message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + headers.remove(MessageHeaders.CONTENT_TYPE); + converted = this.messageConverter.toMessage(message.getPayload(), headers); + } } } - if (converted instanceof Message) { - sentMessage = (Message) converted; - } - else { - sentMessage = MessageConverterConfigurer.this.messageBuilderFactory.withPayload(converted) - .copyHeaders(message.getHeaders()).setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, - - this.mimeType) - .build(); + if (converted != null) { + if (converted instanceof Message) { + sentMessage = (Message) converted; + } + else { + sentMessage = MessageConverterConfigurer.this.messageBuilderFactory.withPayload(converted) + .copyHeaders(message.getHeaders()).setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, + this.mimeType) + .build(); + } } } if (sentMessage == null) { - throw new MessageConversionException("Cannot convert " + message + " to " + this.contentType); + throw new MessageConversionException(message, this.messageConverter.getClass().toString() + + " could not convert '" + message + "' to the configured output type: '" + + this.contentType + "'"); + } return sentMessage; } diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binding/MessageConverterConfigurerTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binding/MessageConverterConfigurerTests.java new file mode 100644 index 000000000..8c17ee69b --- /dev/null +++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binding/MessageConverterConfigurerTests.java @@ -0,0 +1,111 @@ +/* + * 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.binding; + +import java.util.Collections; + +import org.junit.Test; + +import org.springframework.cloud.stream.config.BindingProperties; +import org.springframework.cloud.stream.config.BindingServiceProperties; +import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.converter.AbstractMessageConverter; +import org.springframework.messaging.converter.MessageConversionException; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.util.MimeType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +/** + * @author Gary Russell + * @since 1.3 + * + */ +public class MessageConverterConfigurerTests { + + @Test + public void testConfigureOutputChannelWithBadContentType() { + BindingServiceProperties props = new BindingServiceProperties(); + BindingProperties bindingProps = new BindingProperties(); + bindingProps.setContentType("application/json"); + props.setBindings(Collections.singletonMap("foo", bindingProps)); + CompositeMessageConverterFactory converterFactory = new CompositeMessageConverterFactory( + Collections.emptyList(), null); + MessageConverterConfigurer configurer = new MessageConverterConfigurer(props, converterFactory); + QueueChannel out = new QueueChannel(); + configurer.configureOutputChannel(out, "foo"); + out.send(new GenericMessage(new Foo(), + Collections. singletonMap(MessageHeaders.CONTENT_TYPE, "bad/ct"))); + Message received = out.receive(0); + assertThat(received).isNotNull(); + assertThat(received.getPayload()).isEqualTo("{\"bar\":\"bar\"}"); + } + + @Test + public void testConfigureOutputChannelCannotConvert() { + BindingServiceProperties props = new BindingServiceProperties(); + BindingProperties bindingProps = new BindingProperties(); + bindingProps.setContentType("foo/bar"); + props.setBindings(Collections.singletonMap("foo", bindingProps)); + MessageConverter converter = new AbstractMessageConverter(new MimeType("foo", "bar")) { + + @Override + protected boolean supports(Class clazz) { + return true; + } + + @Override + protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) { + return null; + } + + }; + CompositeMessageConverterFactory converterFactory = new CompositeMessageConverterFactory( + Collections.singletonList(converter), null); + MessageConverterConfigurer configurer = new MessageConverterConfigurer(props, converterFactory); + QueueChannel out = new QueueChannel(); + configurer.configureOutputChannel(out, "foo"); + try { + out.send(new GenericMessage(new Foo(), + Collections. singletonMap(MessageHeaders.CONTENT_TYPE, "bad/ct"))); + fail("Expected MessageConversionException: " + out.receive(0)); + } + catch (MessageConversionException e) { + assertThat(e.getMessage()).endsWith("to the configured output type: 'foo/bar'"); + } + } + + public static class Foo { + + private String bar = "bar"; + + public String getBar() { + return this.bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + + } + +}