INT-3133: Add <spel-property-accessors> Support
* Add <spel-property-accessors> to configure a list of beans that implement `org.springframework.expression.PropertyAccessor` * Add `SpelPropertyAccessorRegistrar` to manage a list of PropertyAccessors for Integration infrastructure * Refactoring for `SpelFunctionRegistrar` and `IntegrationEvaluationContextFactoryBean` to fix the issue when there is no `SpelFunctionRegistrar`(`SpelPropertyAccessorRegistrar`) in the child AC, but there is one in the parent. Previously the inheritance did't work for that reason. * Now parent/child logic moved to `IntegrationEvaluationContextFactoryBean` * Add documentation JIRA: https://jira.springsource.org/browse/INT-3133 INT-3133: Rebasing, polishing and documentation INT-3133: PropertyAccessors override support * `@Ignore` `SOLingerTests#finReceivedNioLinger()` test INT-3133: Change JavaDoc link to 3.0.0.RC1 Polishing - Remove duplicate check - last bean def wins - Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
00d9ba50ef
commit
19dd45a98a
@@ -29,6 +29,7 @@ import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
@@ -40,11 +41,13 @@ import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
@@ -76,6 +79,9 @@ public class OutboundGatewayTests {
|
||||
}
|
||||
}).when(context).getBean(anyString());
|
||||
when(context.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)).thenReturn(true);
|
||||
when(context.getBean(ClassUtils.forName("org.springframework.integration.config.SpelPropertyAccessorRegistrar",
|
||||
context.getClassLoader())))
|
||||
.thenThrow(NoSuchBeanDefinitionException.class);
|
||||
IntegrationEvaluationContextFactoryBean integrationEvaluationContextFactoryBean = new IntegrationEvaluationContextFactoryBean();
|
||||
integrationEvaluationContextFactoryBean.setApplicationContext(context);
|
||||
integrationEvaluationContextFactoryBean.afterPropertiesSet();
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
@@ -42,6 +43,7 @@ import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@link FactoryBean} to populate {@link StandardEvaluationContext} instances enhanced with:
|
||||
* <ul>
|
||||
* <li>
|
||||
@@ -58,8 +60,14 @@ import org.springframework.util.Assert;
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* This factory returns a new instance for each reference singleton - {@link #isSingleton()}
|
||||
* returns false.
|
||||
* <p>
|
||||
* After initialization this factory populates functions and property accessors from
|
||||
* {@link SpelFunctionFactoryBean}s and {@link SpelPropertyAccessorRegistrar}, respectively.
|
||||
* Functions and property accessors are also inherited from any parent context.
|
||||
* </p>
|
||||
* <p>
|
||||
* This factory returns a new instance for each reference - {@link #isSingleton()} returns false.
|
||||
* </p>
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
@@ -75,7 +83,6 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
|
||||
private TypeConverter typeConverter = new StandardTypeConverter();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
@Override
|
||||
@@ -120,6 +127,15 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
|
||||
this.functions.put(spelFunctionFactoryBean.getFunctionName(), spelFunctionFactoryBean.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SpelPropertyAccessorRegistrar propertyAccessorRegistrar = this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class);
|
||||
this.propertyAccessors.addAll(propertyAccessorRegistrar.getPropertyAccessors());
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
// There is no 'SpelPropertyAccessorRegistrar' bean with the parent application context
|
||||
// Ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* http://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.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
|
||||
/**
|
||||
* Utility class that keeps track of a Set of SpEL {@link PropertyAccessor}s
|
||||
* in order to register them with the "integrationEvaluationContext" upon initialization.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
class SpelPropertyAccessorRegistrar implements ApplicationContextAware, InitializingBean {
|
||||
|
||||
private final Map<String, PropertyAccessor> propertyAccessors;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
SpelPropertyAccessorRegistrar(Map<String, PropertyAccessor> propertyAccessors) {
|
||||
this.propertyAccessors = propertyAccessors;
|
||||
}
|
||||
|
||||
Collection<PropertyAccessor> getPropertyAccessors() {
|
||||
return propertyAccessors.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
SpelPropertyAccessorRegistrar parentPropertyAccessorRegistrar = null;
|
||||
try {
|
||||
BeanFactory parentBeanFactory = this.applicationContext.getParentBeanFactory();
|
||||
if (parentBeanFactory != null) {
|
||||
parentPropertyAccessorRegistrar = parentBeanFactory.getBean(SpelPropertyAccessorRegistrar.class);
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
// There is no 'SpelPropertyAccessorRegistrar' bean with the parent application context
|
||||
// Ignore it
|
||||
}
|
||||
|
||||
if (parentPropertyAccessorRegistrar != null) {
|
||||
for (Map.Entry<String, PropertyAccessor> entry : parentPropertyAccessorRegistrar.propertyAccessors.entrySet()) {
|
||||
if (!this.propertyAccessors.containsKey(entry.getKey())) {
|
||||
this.propertyAccessors.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -76,6 +76,7 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan
|
||||
registerBeanDefinitionParser("wire-tap", new GlobalWireTapParser());
|
||||
registerBeanDefinitionParser("transaction-synchronization-factory", new TransactionSynchronizationFactoryParser());
|
||||
registerBeanDefinitionParser("spel-function", new SpelFunctionParser());
|
||||
registerBeanDefinitionParser("spel-property-accessors", new SpelPropertyAccessorsParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* http://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.config.xml;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <spel-property-accessors> element.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelPropertyAccessorsParser implements BeanDefinitionParser {
|
||||
|
||||
private final Map<String, Object> propertyAccessors = new ManagedMap<String, Object>();
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
this.initializeSpelPropertyAccessorRegistrarIfNecessary(parserContext);
|
||||
|
||||
BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
|
||||
|
||||
NodeList children = element.getChildNodes();
|
||||
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node node = children.item(i);
|
||||
String propertyAccessorName = null;
|
||||
Object propertyAccessor = null;
|
||||
if (node instanceof Element && !delegate.nodeNameEquals(node, BeanDefinitionParserDelegate.DESCRIPTION_ELEMENT)) {
|
||||
Element ele = (Element) node;
|
||||
|
||||
if (delegate.nodeNameEquals(ele, BeanDefinitionParserDelegate.BEAN_ELEMENT)) {
|
||||
propertyAccessorName = ele.getAttribute(BeanDefinitionParserDelegate.ID_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(propertyAccessorName)) {
|
||||
parserContext.getReaderContext()
|
||||
.error("The '<bean>' 'id' attribute is required within 'spel-property-accessors'.", ele);
|
||||
return null;
|
||||
}
|
||||
propertyAccessor = delegate.parseBeanDefinitionElement(ele);
|
||||
}
|
||||
else if (delegate.nodeNameEquals(ele, BeanDefinitionParserDelegate.REF_ELEMENT)) {
|
||||
BeanReference propertyAccessorRef = (BeanReference) delegate.parsePropertySubElement(ele, null);
|
||||
propertyAccessorName = propertyAccessorRef.getBeanName();
|
||||
propertyAccessor = propertyAccessorRef;
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("Only '<bean>' and '<ref>' elements are allowed.", element);
|
||||
return null;
|
||||
}
|
||||
|
||||
this.propertyAccessors.put(propertyAccessorName, propertyAccessor);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void initializeSpelPropertyAccessorRegistrarIfNecessary(ParserContext parserContext) {
|
||||
if (!this.initialized) {
|
||||
BeanDefinitionBuilder registrarBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SpelPropertyAccessorRegistrar")
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
.addConstructorArgValue(this.propertyAccessors);
|
||||
BeanDefinitionReaderUtils.registerWithGeneratedName(registrarBuilder.getBeanDefinition(),
|
||||
parserContext.getRegistry());
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3959,6 +3959,21 @@ The list of component name patterns you want to track (e.g., tracked-components
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="spel-property-accessors">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to register PropertyAccessors (implementation of the PropertyAccessor interface) that will
|
||||
be automatically registered with the SpEL EvaluationContext.
|
||||
Note, the 'id' attribute of the bean definition below is required.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:element ref="beans:bean" />
|
||||
<xsd:element ref="beans:ref" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="control-bus-type">
|
||||
<xsd:annotation>
|
||||
|
||||
@@ -23,4 +23,9 @@
|
||||
|
||||
<int:spel-function id="jsonPath" class="org.springframework.integration.expression.ParentContextTests$Bar" method="bar"/>
|
||||
|
||||
<int:spel-property-accessors>
|
||||
<ref bean="jsonPropertyAccessor"/>
|
||||
</int:spel-property-accessors>
|
||||
|
||||
<bean id="jsonPropertyAccessor" class="org.springframework.integration.json.JsonPropertyAccessor"/>
|
||||
</beans>
|
||||
|
||||
@@ -23,4 +23,13 @@
|
||||
|
||||
<int:spel-function id="barParent" class="org.springframework.integration.expression.ParentContextTests$Bar" method="bar"/>
|
||||
|
||||
<int:spel-property-accessors>
|
||||
<ref bean="parentJsonPropertyAccessor"/>
|
||||
<ref bean="jsonPropertyAccessor"/>
|
||||
</int:spel-property-accessors>
|
||||
|
||||
<bean id="jsonPropertyAccessor" class="org.springframework.integration.json.JsonPropertyAccessor"/>
|
||||
|
||||
<bean id="parentJsonPropertyAccessor" class="org.springframework.integration.json.JsonPropertyAccessor"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.integration.expression;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
@@ -27,13 +28,13 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
@@ -45,6 +46,7 @@ import org.springframework.integration.test.util.TestUtils;
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
@@ -57,37 +59,76 @@ public class ParentContextTests {
|
||||
* BeanResolver. Verifies that the two Foos in the parent context get an evaluation context
|
||||
* with the same bean resolver. Verifies that the one Foo in the child context gets a different
|
||||
* bean resolver. Verifies that bean references in SpEL expressions to beans in the child
|
||||
* and parent contexts work.
|
||||
* and parent contexts work. Verifies that PropertyAccessors are inherited in the child context
|
||||
* and the parent's ones are last in the propertyAccessors list of EvaluationContext.
|
||||
* Verifies that SpEL functions are inherited from parent context and overridden with the same 'id'.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSpelBeanReferencesInChildAndParent() throws Exception {
|
||||
AbstractApplicationContext parent = new ClassPathXmlApplicationContext("ParentContext-context.xml", this.getClass());
|
||||
|
||||
Object parentEvaluationContextFactoryBean = parent.getBean(IntegrationEvaluationContextFactoryBean.class);
|
||||
Map parentFunctions = TestUtils.getPropertyValue(parentEvaluationContextFactoryBean, "functions", Map.class);
|
||||
Map<?,?> parentFunctions = TestUtils.getPropertyValue(parentEvaluationContextFactoryBean, "functions", Map.class);
|
||||
assertEquals(3, parentFunctions.size());
|
||||
Object jsonPath = parentFunctions.get("jsonPath");
|
||||
assertNotNull(jsonPath);
|
||||
assertThat((Method) jsonPath, Matchers.isOneOf(JsonPathUtils.class.getMethods()));
|
||||
|
||||
assertEquals(2, evalContexts.size());
|
||||
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(parent);
|
||||
child.setConfigLocation("org/springframework/integration/expression/ChildContext-context.xml");
|
||||
child.refresh();
|
||||
|
||||
Object childEvaluationContextFactoryBean = child.getBean(IntegrationEvaluationContextFactoryBean.class);
|
||||
Map childFunctions = TestUtils.getPropertyValue(childEvaluationContextFactoryBean, "functions", Map.class);
|
||||
Map<?,?> childFunctions = TestUtils.getPropertyValue(childEvaluationContextFactoryBean, "functions", Map.class);
|
||||
assertEquals(4, childFunctions.size());
|
||||
assertTrue(childFunctions.containsKey("barParent"));
|
||||
jsonPath = childFunctions.get("jsonPath");
|
||||
assertNotNull(jsonPath);
|
||||
assertThat((Method) jsonPath, Matchers.not(Matchers.isOneOf(JsonPathUtils.class.getMethods())));
|
||||
|
||||
assertEquals(3, evalContexts.size());
|
||||
assertSame(evalContexts.get(0).getBeanResolver(), evalContexts.get(1).getBeanResolver());
|
||||
|
||||
List<PropertyAccessor> propertyAccessors = evalContexts.get(0).getPropertyAccessors();
|
||||
assertEquals(4, propertyAccessors.size());
|
||||
PropertyAccessor parentPropertyAccessorOverride = parent.getBean("jsonPropertyAccessor", PropertyAccessor.class);
|
||||
PropertyAccessor parentPropertyAccessor = parent.getBean("parentJsonPropertyAccessor", PropertyAccessor.class);
|
||||
assertTrue(propertyAccessors.contains(parentPropertyAccessorOverride));
|
||||
assertTrue(propertyAccessors.contains(parentPropertyAccessor));
|
||||
assertTrue(propertyAccessors.indexOf(parentPropertyAccessorOverride) > propertyAccessors.indexOf(parentPropertyAccessor));
|
||||
|
||||
Map<String, Object> variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(0), "variables");
|
||||
assertEquals(3, variables.size());
|
||||
assertTrue(variables.containsKey("bar"));
|
||||
assertTrue(variables.containsKey("barParent"));
|
||||
assertTrue(variables.containsKey("jsonPath"));
|
||||
|
||||
assertNotSame(evalContexts.get(1).getBeanResolver(), evalContexts.get(2).getBeanResolver());
|
||||
assertSame(parent, TestUtils.getPropertyValue(evalContexts.get(0).getBeanResolver(), "beanFactory"));
|
||||
assertSame(child, TestUtils.getPropertyValue(evalContexts.get(2).getBeanResolver(), "beanFactory"));
|
||||
propertyAccessors = evalContexts.get(1).getPropertyAccessors();
|
||||
assertEquals(4, propertyAccessors.size());
|
||||
assertTrue(propertyAccessors.contains(parentPropertyAccessorOverride));
|
||||
|
||||
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(1), "variables");
|
||||
assertEquals(3, variables.size());
|
||||
assertTrue(variables.containsKey("bar"));
|
||||
assertTrue(variables.containsKey("barParent"));
|
||||
assertTrue(variables.containsKey("jsonPath"));
|
||||
|
||||
propertyAccessors = evalContexts.get(2).getPropertyAccessors();
|
||||
assertEquals(4, propertyAccessors.size());
|
||||
PropertyAccessor childPropertyAccessor = child.getBean("jsonPropertyAccessor", PropertyAccessor.class);
|
||||
assertTrue(propertyAccessors.contains(childPropertyAccessor));
|
||||
assertTrue(propertyAccessors.contains(parentPropertyAccessor));
|
||||
assertFalse(propertyAccessors.contains(parentPropertyAccessorOverride));
|
||||
assertTrue(propertyAccessors.indexOf(childPropertyAccessor) < propertyAccessors.indexOf(parentPropertyAccessor));
|
||||
|
||||
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(2), "variables");
|
||||
assertEquals(4, variables.size());
|
||||
assertTrue(variables.containsKey("bar"));
|
||||
assertTrue(variables.containsKey("barParent"));
|
||||
assertTrue(variables.containsKey("barChild"));
|
||||
assertTrue(variables.containsKey("jsonPath"));
|
||||
|
||||
// Test transformer expressions
|
||||
child.getBean("input", MessageChannel.class).send(new GenericMessage<String>("baz"));
|
||||
|
||||
@@ -39,6 +39,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" />
|
||||
|
||||
<beans:bean id="fooAccessor" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$FooAccessor"/>
|
||||
|
||||
<channel id="fooin" />
|
||||
|
||||
<transformer id="foo" input-channel="fooin" expression="payload.bar" />
|
||||
|
||||
@@ -21,10 +21,13 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -35,11 +38,13 @@ import org.springframework.expression.TypedValue;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.ReplyRequiredException;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -74,6 +79,11 @@ public class SpelTransformerIntegrationTests {
|
||||
@Autowired
|
||||
private MessageChannel spelFunctionInput;
|
||||
|
||||
@Autowired
|
||||
private IntegrationEvaluationContextFactoryBean evaluationContextFactoryBean;
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Test
|
||||
public void simple() {
|
||||
@@ -111,6 +121,7 @@ public class SpelTransformerIntegrationTests {
|
||||
assertNotNull(reply);
|
||||
assertTrue(reply.getPayload() instanceof String);
|
||||
assertEquals("baz", reply.getPayload());
|
||||
assertEquals(4, TestUtils.getPropertyValue(this.evaluationContextFactoryBean, "propertyAccessors", List.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://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>
|
||||
@@ -26,8 +26,10 @@ import java.net.SocketException;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.ip.util.TestingUtilities;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -89,6 +91,7 @@ public class SOLingerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void finReceivedNioLinger() {
|
||||
finReceived(inCFNioLinger);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
<title>SpEL Evaluation Context Customization</title>
|
||||
<para>
|
||||
Starting with Spring Integration 3.0, it is possible to add additional
|
||||
<interfacename>PropertyAccessor</interfacename>s to the SpEL evaluation context.
|
||||
In fact, the framework provides one such accessor,
|
||||
<interfacename>PropertyAccessor</interfacename>s to the SpEL evaluation contexts used by
|
||||
the framework. The framework provides
|
||||
the <classname>JsonPropertyAccessor</classname> which can be used (read-only) to access fields from
|
||||
a <classname>JsonNode</classname>, or JSON in a <classname>String</classname>. Or you can create your
|
||||
own <interfacename>PropertyAccessor</interfacename> if you have specific needs.
|
||||
@@ -47,10 +47,11 @@
|
||||
expression used throughout the framework.
|
||||
</para>
|
||||
<para>
|
||||
To configure your custom accessors and functions, add an
|
||||
<classname>IntegrationEvaluationContextFactoryBean</classname> with
|
||||
<code>id="integrationEvaluationContext"</code>
|
||||
to your application context, with the appropriate configuration; for example:
|
||||
The following configuration shows how to directly configure the
|
||||
<classname>IntegrationEvaluationContextFactoryBean</classname> with custom property accessors
|
||||
and functions. However, for convenience, namespace support is provided for both, as
|
||||
described in the following sections, and the framework will automatically configure
|
||||
the factory bean on your behalf.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<beans:bean id="integrationEvaluationContext"
|
||||
class="org.springframework.integration.config.IntegrationEvaluationContextFactoryBean">
|
||||
@@ -92,8 +93,8 @@
|
||||
<para>
|
||||
Namespace support is provided for easy addition of SpEL custom functions.
|
||||
You can specify <code><spel-function/></code> components to provide
|
||||
<ulink url="http://static.springsource.org/spring-framework/docs/current/spring-framework-reference/html/expressions.html#expressions-ref-functions">
|
||||
custom SpEL functions</ulink> to the <interfacename>EvaluationContext</interfacename> used throughout the framework.
|
||||
<ulink url="http://static.springsource.org/spring-framework/docs/current/spring-framework-reference/html/expressions.html#expressions-ref-functions"
|
||||
>custom SpEL functions</ulink> to the <interfacename>EvaluationContext</interfacename> used throughout the framework.
|
||||
Instead of configuring the factory bean above, simply add one or more of these components
|
||||
and the framework will automatically add them to the default <emphasis>integrationEvaluationContext</emphasis>
|
||||
factory bean.
|
||||
@@ -123,7 +124,7 @@
|
||||
<classname>StandardEvaluationContext</classname> instance,
|
||||
and it is configured with the default
|
||||
<interfacename>PropertyAccessor</interfacename>s, <interfacename>BeanResolver</interfacename>
|
||||
and the custom function.
|
||||
and the custom functions.
|
||||
</listitem>
|
||||
<listitem>
|
||||
That <interfacename>EvaluationContext</interfacename> instance is injected into the
|
||||
@@ -136,13 +137,85 @@
|
||||
SpEL functions declared in a parent context are also made available in any child context(s). Each
|
||||
context has its own instance of the <emphasis>integrationEvaluationContext</emphasis> factory bean
|
||||
because each needs a different <interfacename>BeanResolver</interfacename>, but the function
|
||||
declarations are inherited (and can be overridden if needed by declaring a SpEL function with
|
||||
the same name. The functions themselves are processed by the framework - they do not appear
|
||||
as beans in the application context.
|
||||
declarations are inherited and can be overridden if needed by declaring a SpEL function with
|
||||
the same name.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
Spring Integration provides some standard functions, which are registered with the application
|
||||
context automatically on start up:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<emphasis role="bold">#jsonPath</emphasis> - to evaluate a 'jsonPath' on some provided object. This function
|
||||
invokes <code>JsonPathUtils.evaluate(...)</code>. This static method delegates to the
|
||||
<ulink url="http://code.google.com/p/json-path">Jayway JsonPath library</ulink>. The following shows
|
||||
some usage examples:
|
||||
<programlisting language="xml"><![CDATA[<transformer expression="#jsonPath(payload, '$.store.book[0].author')"/>
|
||||
|
||||
<filter expression="#jsonPath(payload,'$..book[2].isbn') matches '\d-\d{3}-\d{5}-\d'"/>
|
||||
|
||||
<splitter expression="#jsonPath(payload, '$.store.book')"/>
|
||||
|
||||
<router expression="#jsonPath(payload, headers.jsonPath)">
|
||||
<mapping channel="output1" value="reference"/>
|
||||
<mapping channel="output2" value="fiction"/>
|
||||
</router>]]></programlisting>
|
||||
#jsonPath also supports the third optional parameter - an array of
|
||||
<ulink url="https://github.com/jayway/JsonPath/blob/master/json-path/src/main/java/com/jayway/jsonpath/Filter.java"
|
||||
><classname>com.jayway.jsonpath.Filter</classname></ulink>, which could be provided by a reference to a
|
||||
bean or bean method, for example.
|
||||
<note>
|
||||
Using this function requires the Jayway JsonPath library (json-path.jar) to be on
|
||||
the classpath; otherwise the <emphasis>#jsonPath</emphasis>
|
||||
SpEL function won't be registered.
|
||||
</note>
|
||||
For more information regarding JSON see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</listitem>
|
||||
<!--<listitem>
|
||||
<emphasis>#xpath</emphasis> - TBD
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#request</emphasis> - TBD
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#auth</emphasis> - TBD
|
||||
</listitem>-->
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</section>
|
||||
<section id="spel-property-accessors">
|
||||
<title>PropertyAccessors</title>
|
||||
<para>
|
||||
Namespace support is provided for the easy addition of SpEL custom
|
||||
<ulink url="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/expression/PropertyAccessor.html"
|
||||
><interfacename>PropertyAccessor</interfacename></ulink>
|
||||
implementations. You can specify the <code><spel-property-accessors/></code> component to provide a list of
|
||||
custom <interfacename>PropertyAccessor</interfacename>s to the <interfacename>EvaluationContext</interfacename>
|
||||
used throughout the framework. Instead of configuring the factory bean above, simply add one or more of these
|
||||
components, and the framework will automatically add the accessors to the default
|
||||
<emphasis>integrationEvaluationContext</emphasis> factory bean:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int:spel-property-accessors>
|
||||
<bean id="jsonPA" class="org.springframework.integration.json.JsonPropertyAccessor"/>
|
||||
<ref bean="fooPropertyAccessor"/>
|
||||
</int:spel-property-accessors>
|
||||
]]></programlisting>
|
||||
<para>
|
||||
With this sample, two custom <interfacename>PropertyAccessor</interfacename>s will be injected to the
|
||||
<interfacename>EvaluationContext</interfacename> in the order that they are declared.
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
At this time, <interfacename>PropertyAccessor</interfacename>s are not inherited and must be
|
||||
declared as described above in each context.
|
||||
Custom <interfacename>PropertyAccessor</interfacename>s declared in a parent context are also made available
|
||||
in any child context(s). They are placed at the end of result list (but before the default
|
||||
<classname>org.springframework.context.expression.MapAccessor</classname> and
|
||||
<classname>org.springframework.expression.spel.support.ReflectivePropertyAccessor</classname>).
|
||||
If a <interfacename>PropertyAccessor</interfacename> with the same bean id is declared in a child context(s),
|
||||
it will override the parent accessor. Beans declared within a <code><spel-property-accessors/></code>
|
||||
must have an 'id' attribute.
|
||||
The final order of usage is: the accessors in the current context,
|
||||
in the order in which they are declared, followed by any from parent contexts, in order, followed
|
||||
by the <classname>MapAccessor</classname> and finally the <classname>ReflectivePropertyAccessor</classname>.
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -350,6 +350,10 @@ public class Foo {
|
||||
|
||||
<int-amqp:inbound-channel-adapter ... message-converter="jsonConverterWithPOType" ... />]]></programlisting>
|
||||
</important>
|
||||
<para>
|
||||
In addition to JSON Transformers Spring Integration provides out-of-the-box <emphasis>#jsonPath</emphasis>
|
||||
SpEL function. For more information see <xref linkend="spel"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="transformer-annotation">
|
||||
|
||||
@@ -110,6 +110,15 @@
|
||||
component is introduced. For more information see <xref linkend="spel-functions" />.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-spel-property-accessors">
|
||||
<title>SpEL PropertyAccessors Support</title>
|
||||
<para>
|
||||
To customize the SpEL <interfacename>EvaluationContext</interfacename> with
|
||||
<interfacename>PropertyAccessor</interfacename> implementations
|
||||
the new <code><spel-property-accessors/></code> component is introduced.
|
||||
For more information see <xref linkend="spel-property-accessors" />.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="3.0-general">
|
||||
|
||||
Reference in New Issue
Block a user