GH-9436: Add support for SpEL IndexAccessor configuration (#9451)

Fixes: #9436
Issue link: https://github.com/spring-projects/spring-integration/issues/9436

* Expose `IndexAccessor` configuration options on the `AbstractEvaluationContextFactoryBean`
and `SpelPropertyAccessorRegistrar`
* Expose `<index-accessors>` sub-element for the `<spel-property-accessors>`
* Adjust tests
* Document the feature, including recently added `JsonIndexAccessor`
This commit is contained in:
Artem Bilan
2024-09-12 15:38:58 -04:00
committed by GitHub
parent 775bfd6168
commit e3a46ca528
12 changed files with 265 additions and 148 deletions

View File

@@ -64,7 +64,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.log.LogAccessor;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.IndexAccessor;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.annotation.Aggregator;
@@ -112,6 +112,7 @@ import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.history.MessageHistoryConfigurer;
import org.springframework.integration.json.JsonIndexAccessor;
import org.springframework.integration.json.JsonPropertyAccessor;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.integration.support.MessageBuilder;
@@ -786,15 +787,17 @@ public class EnableIntegrationTests {
@Test
public void testIntegrationEvaluationContextCustomization() {
EvaluationContext evaluationContext = this.context.getBean(StandardEvaluationContext.class);
List<?> propertyAccessors = TestUtils.getPropertyValue(evaluationContext, "propertyAccessors", List.class);
StandardEvaluationContext evaluationContext = this.context.getBean(StandardEvaluationContext.class);
List<?> propertyAccessors = evaluationContext.getPropertyAccessors();
assertThat(propertyAccessors.size()).isEqualTo(4);
assertThat(propertyAccessors.get(0)).isInstanceOf(JsonPropertyAccessor.class);
assertThat(propertyAccessors.get(1)).isInstanceOf(EnvironmentAccessor.class);
assertThat(propertyAccessors.get(2)).isInstanceOf(MapAccessor.class);
assertThat(propertyAccessors.get(3)).isInstanceOf(ReflectivePropertyAccessor.class);
Map<?, ?> variables = TestUtils.getPropertyValue(evaluationContext, "variables", Map.class);
Object testSpelFunction = variables.get("testSpelFunction");
List<IndexAccessor> indexAccessors = evaluationContext.getIndexAccessors();
assertThat(indexAccessors.size()).isEqualTo(1);
assertThat(indexAccessors.get(0)).isInstanceOf(JsonIndexAccessor.class);
Object testSpelFunction = evaluationContext.lookupVariable("testSpelFunction");
assertThat(testSpelFunction).isEqualTo(ClassUtils.getStaticMethod(TestSpelFunction.class, "bar",
Object.class));
}
@@ -1244,7 +1247,9 @@ public class EnableIntegrationTests {
@Bean
public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
return new SpelPropertyAccessorRegistrar(new JsonPropertyAccessor(), new EnvironmentAccessor());
return new SpelPropertyAccessorRegistrar(new JsonPropertyAccessor())
.add(new EnvironmentAccessor())
.add(new JsonIndexAccessor());
}
@Bean

View File

@@ -42,10 +42,13 @@
</beans:property>
</beans:bean>
<beans:import resource="property-accessor-import-context.xml" />
<!-- import twice to verify override is ok -->
<beans:import resource="property-accessor-import-context.xml" />
<spel-property-accessors>
<index-accessors>
<beans:bean id="jsonIndex" class="org.springframework.integration.json.JsonIndexAccessor"/>
</index-accessors>
<beans:bean id="fooAccessor1" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$FooAccessor"/>
<beans:ref bean="fooAccessor"/>
</spel-property-accessors>
<beans:bean id="fooAccessor" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$FooAccessor"/>

View File

@@ -16,10 +16,7 @@
package org.springframework.integration.transformer;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -33,13 +30,12 @@ import org.springframework.integration.config.IntegrationEvaluationContextFactor
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat;
@@ -49,8 +45,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Artem Bilan
* @author Gary Russell
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
@DirtiesContext
public class SpelTransformerIntegrationTests {
@Autowired
@@ -96,7 +92,7 @@ public class SpelTransformerIntegrationTests {
@Test
public void testInt2755ChainChildIdWithinExceptionMessage() {
try {
this.transformerChainInput.send(new GenericMessage<String>("foo"));
this.transformerChainInput.send(new GenericMessage<>("foo"));
}
catch (ReplyRequiredException e) {
assertThat(e.getMessage()).contains("No reply produced by handler 'transformerChain$child#0'");
@@ -108,20 +104,20 @@ public class SpelTransformerIntegrationTests {
QueueChannel outputChannel = new QueueChannel();
fooHandler.setOutputChannel(outputChannel);
Foo foo = new Foo("baz");
fooHandler.handleMessage(new GenericMessage<Foo>(foo));
fooHandler.handleMessage(new GenericMessage<>(foo));
Message<?> reply = outputChannel.receive(0);
assertThat(reply).isNotNull();
assertThat(reply.getPayload() instanceof String).isTrue();
assertThat(reply.getPayload()).isEqualTo("baz");
assertThat(TestUtils.getPropertyValue(this.evaluationContextFactoryBean, "propertyAccessors", Map.class).size())
.isEqualTo(3);
assertThat(this.evaluationContextFactoryBean.getPropertyAccessors()).hasSize(3);
assertThat(this.evaluationContextFactoryBean.getIndexAccessors()).hasSize(1);
}
@Test
public void testCustomFunction() {
QueueChannel outputChannel = new QueueChannel();
barHandler.setOutputChannel(outputChannel);
barHandler.handleMessage(new GenericMessage<String>("foo"));
barHandler.handleMessage(new GenericMessage<>("foo"));
Message<?> reply = outputChannel.receive(0);
assertThat(reply).isNotNull();
assertThat(reply.getPayload()).isEqualTo("bar");
@@ -169,7 +165,7 @@ public class SpelTransformerIntegrationTests {
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
public boolean canRead(EvaluationContext context, Object target, String name) {
return "bar".equals(name);
}
@@ -180,7 +176,7 @@ public class SpelTransformerIntegrationTests {
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
public boolean canWrite(EvaluationContext context, Object target, String name) {
return "bar".equals(name);
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
<int:spel-property-accessors>
<bean id="fooAccessor1" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$FooAccessor"/>
<ref bean="fooAccessor"/>
</int:spel-property-accessors>
</beans>