Skip to content

Commit

Permalink
AbstractJackson2MessageConv.: assume supported CT
Browse files Browse the repository at this point in the history
For the JSON and XML converters, assume the content type is supported
if missing or default.
Add option to revert to previous behavior.
  • Loading branch information
garyrussell authored and artembilan committed Jul 17, 2019
1 parent 05ef5a9 commit c1defb5
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 6 deletions.
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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";
Expand Down
Expand Up @@ -187,8 +187,9 @@ public void testNoJsonContentType() {
byte[] bytes = "<root><name>foo</name><root/>".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
Expand Down
8 changes: 7 additions & 1 deletion src/reference/asciidoc/amqp.adoc
Expand Up @@ -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:

Expand Down Expand Up @@ -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.
Expand All @@ -3547,6 +3550,9 @@ The following example configures a `Jackson2JsonMessageConverter`:
----
See <<json-message-converter>> 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`.
Expand Down
6 changes: 6 additions & 0 deletions src/reference/asciidoc/whats-new.adoc
Expand Up @@ -33,6 +33,12 @@ See <<choose-container>> for more information.
Spring Data Projection interfaces are now supported by the `Jackson2JsonMessageConverter`.
See <<data-projection>> 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 <<Jackson2JsonMessageConverter-from-message>> 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 <<jackson2xml>> for more information.

===== AMQP Logging Appenders Changes

The Log4J and Logback `AmqpAppender` s now support a `verifyHostname` SSL option.
Expand Down

0 comments on commit c1defb5

Please sign in to comment.