diff --git a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AbstractJackson2MessageConverter.java b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AbstractJackson2MessageConverter.java index d1820e8081..790f891a18 100644 --- a/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AbstractJackson2MessageConverter.java +++ b/spring-amqp/src/main/java/org/springframework/amqp/support/converter/AbstractJackson2MessageConverter.java @@ -85,6 +85,8 @@ public abstract class AbstractJackson2MessageConverter extends AbstractMessageCo private boolean standardCharset; + private boolean assumeSupportedContentType = true; + /** * Construct with the provided {@link ObjectMapper} instance. * @param objectMapper the {@link ObjectMapper} to use. @@ -218,6 +220,19 @@ public void setUseProjectionForInterfaces(boolean useProjectionForInterfaces) { } } + /** + * By default the supported content type is assumed when there is no contentType + * property or it is set to the default ('application/octet-stream'). Set to 'false' + * to revert to the previous behavior of returning an unconverted 'byte[]' when this + * condition exists. + * @param assumeSupportedContentType set false to not assume the content type is + * supported. + * @since 2.2 + */ + public void setAssumeSupportedContentType(boolean assumeSupportedContentType) { + this.assumeSupportedContentType = assumeSupportedContentType; + } + @Override public Object fromMessage(Message message) throws MessageConversionException { return fromMessage(message, null); @@ -233,7 +248,8 @@ public Object fromMessage(Message message, @Nullable Object conversionHint) thro MessageProperties properties = message.getMessageProperties(); if (properties != null) { String contentType = properties.getContentType(); - if (contentType != null && contentType.contains(this.supportedContentType.getSubtype())) { + if ((this.assumeSupportedContentType && (contentType == null || contentType.equals(MessageProperties.DEFAULT_CONTENT_TYPE))) + || (contentType != null && contentType.contains(this.supportedContentType.getSubtype()))) { String encoding = properties.getContentEncoding(); if (encoding == null) { encoding = getDefaultCharset(); diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2JsonMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2JsonMessageConverterTests.java index a199963d4d..97a514f048 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2JsonMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2JsonMessageConverterTests.java @@ -189,8 +189,9 @@ public void testNoJsonContentType() { byte[] bytes = "{\"name\" : \"foo\" }".getBytes(); MessageProperties messageProperties = new MessageProperties(); Message message = new Message(bytes, messageProperties); - Object foo = jsonConverterWithDefaultType.fromMessage(message); - assertThat(new String((byte[]) foo)).isEqualTo(new String(bytes)); + this.jsonConverterWithDefaultType.setAssumeSupportedContentType(false); + Object foo = this.jsonConverterWithDefaultType.fromMessage(message); + assertThat(foo).isEqualTo(bytes); } @Test @@ -280,6 +281,27 @@ public void testProjection() { assertThat(((Sample) fromMessage).getName()).isEqualTo("SomeName"); } + @Test + public void testMissingContentType() { + byte[] bytes = "{\"name\" : \"foo\" }".getBytes(); + MessageProperties messageProperties = new MessageProperties(); + Message message = new Message(bytes, messageProperties); + Jackson2JsonMessageConverter j2Converter = new Jackson2JsonMessageConverter(); + DefaultClassMapper classMapper = new DefaultClassMapper(); + classMapper.setDefaultType(Foo.class); + j2Converter.setClassMapper(classMapper); + Object foo = j2Converter.fromMessage(message); + assertThat(foo).isInstanceOf(Foo.class); + + messageProperties.setContentType(null); + foo = j2Converter.fromMessage(message); + assertThat(foo).isInstanceOf(Foo.class); + + j2Converter.setAssumeSupportedContentType(false); + foo = j2Converter.fromMessage(message); + assertThat(foo).isSameAs(bytes); + } + public static class Foo { private String name = "foo"; diff --git a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2XmlMessageConverterTests.java b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2XmlMessageConverterTests.java index 06de80f251..43539a6b2e 100644 --- a/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2XmlMessageConverterTests.java +++ b/spring-amqp/src/test/java/org/springframework/amqp/support/converter/Jackson2XmlMessageConverterTests.java @@ -187,8 +187,9 @@ public void testNoJsonContentType() { byte[] bytes = "foo".getBytes(); MessageProperties messageProperties = new MessageProperties(); Message message = new Message(bytes, messageProperties); - Object foo = xmlConverterWithDefaultType.fromMessage(message); - assertThat(new String(bytes)).isEqualTo(new String((byte[]) foo)); + this.xmlConverterWithDefaultType.setAssumeSupportedContentType(false); + Object foo = this.xmlConverterWithDefaultType.fromMessage(message); + assertThat(foo).isEqualTo(bytes); } @Test diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index 8df15f2c33..442434d05a 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -3398,7 +3398,9 @@ for convenience.) If you inject a custom type mapper, you should set the property on the mapper instead. NOTE: When converting from the `Message`, an incoming `MessageProperties.getContentType()` must be JSON-compliant (`contentType.contains("json")` is used to check). -Otherwise, a `WARN` log message `Could not convert incoming message with content-type [...]`, is emitted and `message.getBody()` is returned as is -- as a `byte[]`. +Starting with version 2.2, `application/json` is assumed if there is no `contentType` property, or it has the default value `application/octet-stream`. +To revert to the previous behavior (return an unconverted `byte[]`), set the converter's `assumeSupportedContentType` property to `false`. +If the content type is not supported, a `WARN` log message `Could not convert incoming message with content-type [...]`, is emitted and `message.getBody()` is returned as is -- as a `byte[]`. So, to meet the `Jackson2JsonMessageConverter` requirements on the consumer side, the producer must add the `contentType` message property -- for example, as `application/json` or `text/x-json` or by using the `Jackson2JsonMessageConverter`, which sets the header automatically. The following listing shows a number of converter calls: @@ -3521,6 +3523,7 @@ The following example shows how to configure a `MarshallingMessageConverter`: ---- ==== +[[jackson2xml]] ===== `Jackson2XmlMessageConverter` This class was introduced in version 2.1 and can be used to convert messages from and to XML. @@ -3547,6 +3550,9 @@ The following example configures a `Jackson2JsonMessageConverter`: ---- See <> for more information. +NOTE: Starting with version 2.2, `application/xml` is assumed if there is no `contentType` property, or it has the default value `application/octet-stream`. +To revert to the previous behavior (return an unconverted `byte[]`), set the converter's `assumeSupportedContentType` property to `false`. + ===== `ContentTypeDelegatingMessageConverter` This class was introduced in version 1.4.2 and allows delegation to a specific `MessageConverter` based on the content type property in the `MessageProperties`. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index e8f533a2c8..cff87bf8bf 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -33,6 +33,12 @@ See <> for more information. Spring Data Projection interfaces are now supported by the `Jackson2JsonMessageConverter`. See <> for more information. +The `Jackson2JsonMessageConverter` now assumes the content is JSON if there is no `contentType` property, or it is the default (`application/octet-string`). +See <> for more information. + +Similarly. the `Jackson2XmlMessageConverter` now assumes the content is XML if there is no `contentType` property, or it is the default (`application/octet-string`). +See <> for more information. + ===== AMQP Logging Appenders Changes The Log4J and Logback `AmqpAppender` s now support a `verifyHostname` SSL option.