INT-3874: Copy spel-functions from parent ctx

JIRA: https://jira.spring.io/browse/INT-3874

Previously the `IntegrationEvaluationContextFactoryBean` copied only those spel-functions which are defined
as beans in the ctx. Those which are specified through the `setFunctions` hasn't been copied from parent ctx to child.

This fix addresses and issue.
This commit is contained in:
Artem Bilan
2015-11-03 16:51:45 -05:00
parent 9918304954
commit 7edef1d9f1
4 changed files with 38 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2015 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.
@@ -101,7 +101,7 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
}
public Map<String, PropertyAccessor> getPropertyAccessors() {
return propertyAccessors;
return this.propertyAccessors;
}
public void setFunctions(Map<String, Method> functionsArg) {
@@ -111,6 +111,10 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
this.functions = new LinkedHashMap<String, Method>(functionsArg);
}
public Map<String, Method> getFunctions() {
return this.functions;
}
public void setTypeLocator(TypeLocator typeLocator) {
this.typeLocator = typeLocator;
}
@@ -134,7 +138,8 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
}
try {
SpelPropertyAccessorRegistrar propertyAccessorRegistrar = this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class);
SpelPropertyAccessorRegistrar propertyAccessorRegistrar =
this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class);
for (Entry<String, PropertyAccessor> entry : propertyAccessorRegistrar.getPropertyAccessors().entrySet()) {
if (!this.propertyAccessors.containsKey(entry.getKey())) {
this.propertyAccessors.put(entry.getKey(), entry.getValue());
@@ -157,6 +162,12 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
this.propertyAccessors.put(entry.getKey(), entry.getValue());
}
}
for (Entry<String, Method> entry : parentFactoryBean.getFunctions().entrySet()) {
if (!this.functions.containsKey(entry.getKey())) {
this.functions.put(entry.getKey(), entry.getValue());
}
}
}
}

View File

@@ -21,6 +21,16 @@
<bean class="org.springframework.integration.expression.ParentContextTests$Foo" />
<bean id="integrationEvaluationContext"
class="org.springframework.integration.config.IntegrationEvaluationContextFactoryBean">
<property name="functions">
<map>
<entry key="fooFunc"
value="#{T(org.springframework.integration.expression.ParentContextTests$Bar).getMethod('bar', T(Object))}"/>
</map>
</property>
</bean>
<int:spel-function id="bar" class="org.springframework.integration.expression.ParentContextTests$Bar" method="bar"/>
<int:spel-function id="barParent" class="org.springframework.integration.expression.ParentContextTests$Bar" method="bar"/>

View File

@@ -33,6 +33,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -53,9 +56,6 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.hamcrest.Matchers;
import org.junit.Test;
/**
* @author Gary Russell
* @author Artem Bilan
@@ -87,10 +87,10 @@ public class ParentContextTests {
Object parentEvaluationContextFactoryBean = parent.getBean(IntegrationEvaluationContextFactoryBean.class);
Map<?, ?> parentFunctions = TestUtils.getPropertyValue(parentEvaluationContextFactoryBean, "functions",
Map.class);
assertEquals(3, parentFunctions.size());
assertEquals(4, parentFunctions.size());
Object jsonPath = parentFunctions.get("jsonPath");
assertNotNull(jsonPath);
assertThat((Method) jsonPath, Matchers.isOneOf(JsonPathUtils.class.getMethods()));
assertThat(jsonPath, Matchers.isOneOf(JsonPathUtils.class.getMethods()));
assertEquals(2, evalContexts.size());
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(parent);
child.setConfigLocation("org/springframework/integration/expression/ChildContext-context.xml");
@@ -99,8 +99,9 @@ public class ParentContextTests {
Object childEvaluationContextFactoryBean = child.getBean(IntegrationEvaluationContextFactoryBean.class);
Map<?, ?> childFunctions = TestUtils.getPropertyValue(childEvaluationContextFactoryBean, "functions",
Map.class);
assertEquals(4, childFunctions.size());
assertEquals(5, childFunctions.size());
assertTrue(childFunctions.containsKey("barParent"));
assertTrue(childFunctions.containsKey("fooFunc"));
jsonPath = childFunctions.get("jsonPath");
assertNotNull(jsonPath);
assertThat((Method) jsonPath, Matchers.not(Matchers.isOneOf(JsonPathUtils.class.getMethods())));
@@ -117,9 +118,10 @@ public class ParentContextTests {
Map<String, Object> variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(0),
"variables");
assertEquals(3, variables.size());
assertEquals(4, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("jsonPath"));
assertNotSame(evalContexts.get(1).getBeanResolver(), evalContexts.get(2).getBeanResolver());
@@ -128,9 +130,10 @@ public class ParentContextTests {
assertTrue(propertyAccessors.contains(parentPropertyAccessorOverride));
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(1), "variables");
assertEquals(3, variables.size());
assertEquals(4, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("jsonPath"));
propertyAccessors = evalContexts.get(2).getPropertyAccessors();
@@ -142,9 +145,10 @@ public class ParentContextTests {
assertTrue(propertyAccessors.indexOf(childPropertyAccessor) < propertyAccessors.indexOf(parentPropertyAccessor));
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(2), "variables");
assertEquals(4, variables.size());
assertEquals(5, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("barChild"));
assertTrue(variables.containsKey("jsonPath"));

View File

@@ -57,7 +57,7 @@ In the above example, the custom function is a static method `calc` on class `My
Say you have a `Message` with a payload that has a type `MyFoo` on which you need to perform some action to create a `MyBar` object from it, and you then want to invoke a custom function `calc` on that object.
The standard property accessors wouldn't know how to get a `MyBar` from a `MyFoo` so you could write and configure a custom property accessor to do so.
So, your final expression might be`"#barcalc(payload.myBar)"`.
So, your final expression might be `"#barcalc(payload.myBar)"`.
The factory bean has another property `typeLocator` which allows you to customize the `TypeLocator` used during SpEL evaluation.
This might be necessary when running in some environments that use a non-standard `ClassLoader`.