Use bean CL for JdbcMessageStore.deserializer
Related to https://stackoverflow.com/questions/72305387/spring-integration-delayer-starts-sending-null-message-payloads-when-switched In some async use-cases (e.g. `DelayHandler`), the context classloader might be different for the data to be deserialized from message store. * Fix `JdbcMessageStore` to populate a bean `ClassLoader` into default `AllowListDeserializingConverter` from the application context. The provided `Deserializer` must ensure such a `ClassLoader` itself * Add warning message to the `LambdaMessageProcessor` when converter returns `null` for the payload it cannot convert to expected type. Cannot be raised as error since some applications may already rely on the `null` conversion result in their method arguments **Cherry-pick to `5.5.x`**
This commit is contained in:
committed by
Gary Russell
parent
469bd6ba04
commit
9f4f91d5f9
@@ -29,6 +29,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -151,7 +152,14 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
|
||||
args[i] = message;
|
||||
}
|
||||
else {
|
||||
args[i] = this.messageConverter.fromMessage(message, this.expectedType);
|
||||
Object payload = this.messageConverter.fromMessage(message, this.expectedType);
|
||||
if (payload == null && LOGGER.isWarnEnabled()) {
|
||||
LOGGER.warn(LogMessage.format(
|
||||
"The '%s' returned 'null' for the payload conversion from the " +
|
||||
"'%s' and expected type '%s'.",
|
||||
this.messageConverter, message, this.expectedType));
|
||||
}
|
||||
args[i] = payload;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -41,6 +41,9 @@ import org.springframework.util.PatternMatchUtils;
|
||||
* classes/packages are deserialized. If you receive data from untrusted sources, consider
|
||||
* adding trusted classes/packages using {@link #setAllowedPatterns(String...)} or
|
||||
* {@link #addAllowedPatterns(String...)}.
|
||||
* <p>
|
||||
* If a delegate deserializer is a {@link DefaultDeserializer}, only its {@link ClassLoader}
|
||||
* is used for a {@link ConfigurableObjectInputStream} logic.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Mark Fisher
|
||||
@@ -133,7 +136,13 @@ public class AllowListDeserializingConverter implements Converter<byte[], Object
|
||||
return deserialize(byteStream);
|
||||
}
|
||||
else {
|
||||
return this.deserializer.deserialize(byteStream);
|
||||
Object result = this.deserializer.deserialize(byteStream);
|
||||
/* Even if there is no knowledge what is the target deserialization algorithm
|
||||
and malicious code may be executed already, it is still better to fail
|
||||
with untrusted data rather than just let it pass downstream.
|
||||
*/
|
||||
checkAllowList(result.getClass());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
* Copyright 2016-2022 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.dsl;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -31,6 +32,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.IntegrationConverter;
|
||||
import org.springframework.integration.core.GenericSelector;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
import org.springframework.integration.handler.LambdaMessageProcessor;
|
||||
import org.springframework.integration.transformer.GenericTransformer;
|
||||
@@ -85,6 +87,24 @@ public class LambdaMessageProcessorTests {
|
||||
.isThrownBy(() -> lmp.processMessage(testMessage));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConversionToNull() {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(
|
||||
new GenericSelector<Date>() { // Must not be lambda
|
||||
|
||||
@Override
|
||||
public boolean accept(Date payload) {
|
||||
return payload == null;
|
||||
}
|
||||
|
||||
}, Date.class);
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
GenericMessage<String> testMessage = new GenericMessage<>("foo");
|
||||
Object result = lmp.processMessage(testMessage);
|
||||
assertThat(result).isEqualTo(Boolean.TRUE);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomConverter() {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class);
|
||||
|
||||
Reference in New Issue
Block a user