From 19dd45a98a314d0a1eba9a2be23d5641d4701787 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Fri, 6 Sep 2013 10:49:53 +0300 Subject: [PATCH] INT-3133: Add Support * Add 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 --- .../amqp/config/OutboundGatewayTests.java | 6 ++ ...tegrationEvaluationContextFactoryBean.java | 22 +++- .../config/SpelPropertyAccessorRegistrar.java | 79 ++++++++++++++ .../xml/IntegrationNamespaceHandler.java | 1 + .../xml/SpelPropertyAccessorsParser.java | 100 +++++++++++++++++ .../config/xml/spring-integration-3.0.xsd | 15 +++ .../expression/ChildContext-context.xml | 5 + .../expression/ParentContext-context.xml | 9 ++ .../expression/ParentContextTests.java | 59 ++++++++-- ...pelTransformerIntegrationTests-context.xml | 7 ++ .../SpelTransformerIntegrationTests.java | 11 ++ .../property-accessor-import-context.xml | 14 +++ .../ip/tcp/connection/SOLingerTests.java | 3 + src/reference/docbook/spel.xml | 101 +++++++++++++++--- src/reference/docbook/transformer.xml | 4 + src/reference/docbook/whats-new.xml | 9 ++ 16 files changed, 419 insertions(+), 26 deletions(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/SpelPropertyAccessorRegistrar.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelPropertyAccessorsParser.java create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/transformer/property-accessor-import-context.xml diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java index 9036b31854..28c65f167e 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java @@ -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(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java index 90c10c7742..8cae62262b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java @@ -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; /** + *

* {@link FactoryBean} to populate {@link StandardEvaluationContext} instances enhanced with: *

    *
  • @@ -58,8 +60,14 @@ import org.springframework.util.Assert; *
  • *
*

- * This factory returns a new instance for each reference singleton - {@link #isSingleton()} - * returns false. + *

+ * 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. + *

+ *

+ * This factory returns a new instance for each reference - {@link #isSingleton()} returns false. + *

* * @author Artem Bilan * @author Gary Russell @@ -75,7 +83,6 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean propertyAccessors; + + private ApplicationContext applicationContext; + + SpelPropertyAccessorRegistrar(Map propertyAccessors) { + this.propertyAccessors = propertyAccessors; + } + + Collection 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 entry : parentPropertyAccessorRegistrar.propertyAccessors.entrySet()) { + if (!this.propertyAccessors.containsKey(entry.getKey())) { + this.propertyAccessors.put(entry.getKey(), entry.getValue()); + } + } + } + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java index cacddb2c69..2b4d108afd 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java @@ -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()); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelPropertyAccessorsParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelPropertyAccessorsParser.java new file mode 100644 index 0000000000..13e355985d --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelPropertyAccessorsParser.java @@ -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 propertyAccessors = new ManagedMap(); + + 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 '' '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 '' and '' 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; + } + } + +} diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd index 7f469dec69..ed596cf9e7 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd @@ -3959,6 +3959,21 @@ The list of component name patterns you want to track (e.g., tracked-components + + + + 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. + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml index 0243875dd6..fa7ca83dab 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml @@ -23,4 +23,9 @@ + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml index 6eb178c80a..583b7727c5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml @@ -23,4 +23,13 @@ + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java index 961f90db20..ed0cae5b63 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java @@ -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 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 variables = (Map) 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) 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) 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("baz")); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml index 1e75c00426..603036cf5c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml @@ -39,6 +39,13 @@ + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java index e753c1ab17..d41159978a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java @@ -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 diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/property-accessor-import-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/transformer/property-accessor-import-context.xml new file mode 100644 index 0000000000..9849a70241 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/property-accessor-import-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/SOLingerTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/SOLingerTests.java index ffed22a4b2..4c91f74056 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/SOLingerTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/SOLingerTests.java @@ -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); } diff --git a/src/reference/docbook/spel.xml b/src/reference/docbook/spel.xml index 809d444a8a..c8fa0a8c36 100644 --- a/src/reference/docbook/spel.xml +++ b/src/reference/docbook/spel.xml @@ -35,8 +35,8 @@ SpEL Evaluation Context Customization Starting with Spring Integration 3.0, it is possible to add additional - PropertyAccessors to the SpEL evaluation context. - In fact, the framework provides one such accessor, + PropertyAccessors to the SpEL evaluation contexts used by + the framework. The framework provides the JsonPropertyAccessor which can be used (read-only) to access fields from a JsonNode, or JSON in a String. Or you can create your own PropertyAccessor if you have specific needs. @@ -47,10 +47,11 @@ expression used throughout the framework. - To configure your custom accessors and functions, add an - IntegrationEvaluationContextFactoryBean with - id="integrationEvaluationContext" - to your application context, with the appropriate configuration; for example: + The following configuration shows how to directly configure the + IntegrationEvaluationContextFactoryBean 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. @@ -92,8 +93,8 @@ Namespace support is provided for easy addition of SpEL custom functions. You can specify <spel-function/> components to provide - - custom SpEL functions to the EvaluationContext used throughout the framework. + custom SpEL functions to the EvaluationContext 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 integrationEvaluationContext factory bean. @@ -123,7 +124,7 @@ StandardEvaluationContext instance, and it is configured with the default PropertyAccessors, BeanResolver - and the custom function. + and the custom functions. That EvaluationContext 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 integrationEvaluationContext factory bean because each needs a different BeanResolver, 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. + + + Spring Integration provides some standard functions, which are registered with the application + context automatically on start up: + + + #jsonPath - to evaluate a 'jsonPath' on some provided object. This function + invokes JsonPathUtils.evaluate(...). This static method delegates to the + Jayway JsonPath library. The following shows + some usage examples: + + + + + + + + + +]]> + #jsonPath also supports the third optional parameter - an array of + com.jayway.jsonpath.Filter, which could be provided by a reference to a + bean or bean method, for example. + + Using this function requires the Jayway JsonPath library (json-path.jar) to be on + the classpath; otherwise the #jsonPath + SpEL function won't be registered. + + For more information regarding JSON see 'JSON Transformers' in . + + + + + +
+ PropertyAccessors + + Namespace support is provided for the easy addition of SpEL custom + PropertyAccessor + implementations. You can specify the <spel-property-accessors/> component to provide a list of + custom PropertyAccessors to the EvaluationContext + 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 + integrationEvaluationContext factory bean: + + + + + + ]]> + + With this sample, two custom PropertyAccessors will be injected to the + EvaluationContext in the order that they are declared. + + - At this time, PropertyAccessors are not inherited and must be - declared as described above in each context. + Custom PropertyAccessors 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 + org.springframework.context.expression.MapAccessor and + org.springframework.expression.spel.support.ReflectivePropertyAccessor). + If a PropertyAccessor with the same bean id is declared in a child context(s), + it will override the parent accessor. Beans declared within a <spel-property-accessors/> + 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 MapAccessor and finally the ReflectivePropertyAccessor.
diff --git a/src/reference/docbook/transformer.xml b/src/reference/docbook/transformer.xml index a5fccb0edf..a3a061eb71 100644 --- a/src/reference/docbook/transformer.xml +++ b/src/reference/docbook/transformer.xml @@ -350,6 +350,10 @@ public class Foo { ]]>
+ + In addition to JSON Transformers Spring Integration provides out-of-the-box #jsonPath + SpEL function. For more information see . +
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index a4fe0fd437..a032b884e0 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -110,6 +110,15 @@ component is introduced. For more information see .
+
+ SpEL PropertyAccessors Support + + To customize the SpEL EvaluationContext with + PropertyAccessor implementations + the new <spel-property-accessors/> component is introduced. + For more information see . + +