diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/AllowListDeserializingConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/AllowListDeserializingConverter.java index f0af9976c5..fa44f84ca1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/AllowListDeserializingConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/AllowListDeserializingConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -30,6 +30,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.serializer.DefaultDeserializer; import org.springframework.core.serializer.Deserializer; import org.springframework.core.serializer.support.SerializationFailedException; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; @@ -55,6 +56,7 @@ public class AllowListDeserializingConverter implements Converter deserializer; + @Nullable private final ClassLoader defaultDeserializerClassLoader; private final boolean usingDefaultDeserializer; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/DefaultDatatypeChannelMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/DefaultDatatypeChannelMessageConverter.java index bf5e9b49d8..d543493e0e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/DefaultDatatypeChannelMessageConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/DefaultDatatypeChannelMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2023 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. @@ -22,6 +22,7 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.integration.support.utils.IntegrationUtils; +import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; @@ -33,22 +34,21 @@ import org.springframework.util.Assert; * if present. * * @author Gary Russell + * * @since 4.0 * */ -public class DefaultDatatypeChannelMessageConverter implements MessageConverter, - BeanFactoryAware { +public class DefaultDatatypeChannelMessageConverter implements MessageConverter, BeanFactoryAware { - private volatile ConversionService conversionService = DefaultConversionService.getSharedInstance(); + private ConversionService conversionService = DefaultConversionService.getSharedInstance(); - private volatile boolean conversionServiceSet; + private boolean conversionServiceSet; /** * Specify the {@link ConversionService} to use when trying to convert to * requested type. If this property is not set explicitly but * the converter is managed within a context, it will attempt to locate a * bean named "integrationConversionService" defined within that context. - * * @param conversionService The conversion service. */ public void setConversionService(ConversionService conversionService) { @@ -59,7 +59,7 @@ public class DefaultDatatypeChannelMessageConverter implements MessageConverter, @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - if (!this.conversionServiceSet && beanFactory != null) { + if (!this.conversionServiceSet) { ConversionService integrationConversionService = IntegrationUtils.getConversionService(beanFactory); if (integrationConversionService != null) { this.conversionService = integrationConversionService; @@ -81,7 +81,7 @@ public class DefaultDatatypeChannelMessageConverter implements MessageConverter, } @Override - public Message toMessage(Object payload, MessageHeaders header) { + public Message toMessage(Object payload, @Nullable MessageHeaders header) { throw new UnsupportedOperationException("This converter does not support this method"); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/MapMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/MapMessageConverter.java index a03e0d4f7e..09812e8507 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/MapMessageConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/MapMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -31,6 +31,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Converts to/from a Map with 2 keys ('headers' and 'payload'). @@ -43,15 +44,15 @@ import org.springframework.util.Assert; */ public class MapMessageConverter implements MessageConverter, BeanFactoryAware { - private volatile String[] headerNames; + private String[] headerNames = {}; - private volatile boolean filterHeadersInToMessage; + private boolean filterHeadersInToMessage; - private volatile BeanFactory beanFactory; + private BeanFactory beanFactory; - private volatile MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); + private MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); - private volatile boolean messageBuilderFactorySet; + private boolean messageBuilderFactorySet; @Override @@ -77,16 +78,19 @@ public class MapMessageConverter implements MessageConverter, BeanFactoryAware { * @param headerNames The header names. */ public void setHeaderNames(String... headerNames) { - Assert.notEmpty(headerNames, "at least one header name is required"); - this.headerNames = Arrays.copyOf(headerNames, headerNames.length); + if (ObjectUtils.isEmpty(headerNames)) { + this.headerNames = new String[] {}; + } + else { + this.headerNames = Arrays.copyOf(headerNames, headerNames.length); + } } /** - * By default all headers on Map passed to {@link #toMessage(Object, MessageHeaders)} + * By default, all headers on Map passed to {@link #toMessage(Object, MessageHeaders)} * will be mapped. Set this property - * to 'true' if you wish to limit the inbound headers to those in - * the #headerNames. - * + * to 'true' if you wish to limit the inbound headers to those + * in the {@link #headerNames}. * @param filterHeadersInToMessage true if the headers should be filtered. */ public void setFilterHeadersInToMessage(boolean filterHeadersInToMessage) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ObjectStringMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ObjectStringMessageConverter.java index b3973359af..d6ae3b6c35 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ObjectStringMessageConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ObjectStringMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2023 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. @@ -16,6 +16,9 @@ package org.springframework.integration.support.converter; +import java.nio.charset.Charset; + +import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.converter.StringMessageConverter; @@ -24,16 +27,31 @@ import org.springframework.messaging.converter.StringMessageConverter; *

* Delegates to super when payload is {@code byte[]} or {@code String}. * Performs {@link Object#toString()} in other cases. + *

+ * This class is intended to serve as a fallback converter for internal message deserialization purposes. Therefore, it + * is recommended to exclusively use the + * {@link org.springframework.messaging.converter.AbstractMessageConverter#fromMessage(Message, Class) fromMessage} + * method with {@code String.class} as the {@code targetClass}. * * @author Marius Bogoevici * @author Artem Bilan + * @author Falk Hanisch * * @since 5.0 */ public class ObjectStringMessageConverter extends StringMessageConverter { + public ObjectStringMessageConverter(Charset defaultCharset) { + super(defaultCharset); + } + + public ObjectStringMessageConverter() { + super(); + } + + @Override - protected Object convertFromInternal(Message message, Class targetClass, Object conversionHint) { + protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { Object payload = message.getPayload(); if (payload instanceof String || payload instanceof byte[]) { return super.convertFromInternal(message, targetClass, conversionHint); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/PassThruMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/PassThruMessageConverter.java index 8665e270ec..2ec1e498de 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/PassThruMessageConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/PassThruMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 the original author or authors. + * Copyright 2015-2023 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. @@ -16,11 +16,11 @@ package org.springframework.integration.support.converter; +import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; /** * The simple {@link MessageConverter} implementation which contract is to return @@ -31,6 +31,7 @@ import org.springframework.util.Assert; * operates only with {@link Message}s, e.g. Spring Integration Adapters. * * @author Artem Bilan + * * @since 4.2 */ public class PassThruMessageConverter implements MessageConverter { @@ -41,9 +42,13 @@ public class PassThruMessageConverter implements MessageConverter { } @Override - public Message toMessage(Object payload, MessageHeaders headers) { - Assert.isInstanceOf(byte[].class, payload, "'payload' must be of 'byte[]' type."); - return MessageBuilder.createMessage(payload, headers); + public Message toMessage(Object payload, @Nullable MessageHeaders headers) { + if (payload instanceof byte[]) { + return MessageBuilder.withPayload(payload).copyHeaders(headers).build(); + } + else { + return null; + } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/SimpleMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/SimpleMessageConverter.java index c07cb09ef7..a478e8c373 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/SimpleMessageConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/SimpleMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -42,51 +42,50 @@ import org.springframework.messaging.converter.MessageConverter; @SuppressWarnings({"unchecked", "rawtypes"}) public class SimpleMessageConverter implements MessageConverter, BeanFactoryAware { - private volatile InboundMessageMapper inboundMessageMapper; + private InboundMessageMapper inboundMessageMapper = new DefaultInboundMessageMapper(); - private volatile OutboundMessageMapper outboundMessageMapper; + private OutboundMessageMapper outboundMessageMapper = new DefaultOutboundMessageMapper(); - private volatile MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); + private MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); - private volatile boolean messageBuilderFactorySet; + private boolean messageBuilderFactorySet; private BeanFactory beanFactory; - public SimpleMessageConverter() { - this(null, null); - } + public SimpleMessageConverter() { } public SimpleMessageConverter(InboundMessageMapper inboundMessageMapper) { - this(inboundMessageMapper, - (inboundMessageMapper instanceof OutboundMessageMapper - ? (OutboundMessageMapper) inboundMessageMapper - : null)); + setInboundMessageMapper(inboundMessageMapper); + if (inboundMessageMapper instanceof OutboundMessageMapper messageMapper) { + setOutboundMessageMapper(messageMapper); + } } public SimpleMessageConverter(OutboundMessageMapper outboundMessageMapper) { - this(outboundMessageMapper instanceof InboundMessageMapper - ? (InboundMessageMapper) outboundMessageMapper - : null, - outboundMessageMapper); + if (outboundMessageMapper instanceof InboundMessageMapper messageMapper) { + setInboundMessageMapper(messageMapper); + } + setOutboundMessageMapper(outboundMessageMapper); } public SimpleMessageConverter(InboundMessageMapper inboundMessageMapper, OutboundMessageMapper outboundMessageMapper) { - this.setInboundMessageMapper(inboundMessageMapper); - this.setOutboundMessageMapper(outboundMessageMapper); + + setInboundMessageMapper(inboundMessageMapper); + setOutboundMessageMapper(outboundMessageMapper); } - public final void setInboundMessageMapper(InboundMessageMapper inboundMessageMapper) { - this.inboundMessageMapper = (inboundMessageMapper != null) - ? inboundMessageMapper - : new DefaultInboundMessageMapper(); + public final void setInboundMessageMapper(@Nullable InboundMessageMapper inboundMessageMapper) { + if (inboundMessageMapper != null) { + this.inboundMessageMapper = inboundMessageMapper; + } } - public final void setOutboundMessageMapper(OutboundMessageMapper outboundMessageMapper) { - this.outboundMessageMapper = (outboundMessageMapper != null - ? outboundMessageMapper - : new DefaultOutboundMessageMapper()); + public final void setOutboundMessageMapper(@Nullable OutboundMessageMapper outboundMessageMapper) { + if (outboundMessageMapper != null) { + this.outboundMessageMapper = outboundMessageMapper; + } } @Override @@ -110,8 +109,8 @@ public class SimpleMessageConverter implements MessageConverter, BeanFactoryAwar try { return this.inboundMessageMapper.toMessage(object, headers); } - catch (Exception e) { - throw new MessageConversionException("failed to convert object to Message", e); + catch (Exception ex) { + throw new MessageConversionException("failed to convert object to Message", ex); } } @@ -121,8 +120,8 @@ public class SimpleMessageConverter implements MessageConverter, BeanFactoryAwar try { return this.outboundMessageMapper.fromMessage(message); } - catch (Exception e) { - throw new MessageConversionException(message, "failed to convert Message to object", e); + catch (Exception ex) { + throw new MessageConversionException(message, "failed to convert Message to object", ex); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/package-info.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/package-info.java index 8a19baafb2..9cae5c1d82 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/package-info.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/package-info.java @@ -1,4 +1,6 @@ /** * Provides classes supporting message conversion. */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields package org.springframework.integration.support.converter; diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/converter/ObjectStringMessageConverterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/converter/ObjectStringMessageConverterTests.java new file mode 100644 index 0000000000..6dca7ae391 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/converter/ObjectStringMessageConverterTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2023 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 + * + * https://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.integration.support.converter; + + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Falk Hanisch + * + * @since 6.2 + */ +class ObjectStringMessageConverterTests { + + private static final ArbitraryPayload PAYLOAD = new ArbitraryPayload(1234, "ÄÖÜ"); + + @Test + void testFromMessage() { + Message message = MessageBuilder.withPayload(PAYLOAD).build(); + ObjectStringMessageConverter converter = new ObjectStringMessageConverter(); + Object result = converter.fromMessage(message, String.class); + + assertThat(message.getPayload()).isEqualTo(PAYLOAD); + assertThat(result).isEqualTo(PAYLOAD.toString()); + + result = converter.fromMessage(message, Object.class); + assertThat(result).isNull(); + } + + @Test + void testToMessage() { + String text = PAYLOAD.text(); + ObjectStringMessageConverter converter = new ObjectStringMessageConverter(StandardCharsets.ISO_8859_1); + Message converted = converter.toMessage(text, null); + + assertThat(converted).isNotNull(); + assertThat(converted.getPayload()).isEqualTo(text.getBytes(StandardCharsets.ISO_8859_1)); + + converter.setSerializedPayloadClass(String.class); + converted = converter.toMessage(text, null); + assertThat(converted).isNotNull(); + assertThat(converted.getPayload()).isEqualTo(text); + + converted = converter.toMessage(PAYLOAD, null); + assertThat(converted).isNull(); + } + + private record ArbitraryPayload(Integer id, String text) { + } + +}