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 e2387a9b4c..68f34872fc 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-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.
@@ -44,6 +44,8 @@ import org.springframework.test.util.ReflectionTestUtils;
/**
* @author Mark Fisher
* @author Dave Syer
+ * @author Gary Russell
+ *
* @since 2.1
*/
public class OutboundGatewayTests {
@@ -75,7 +77,7 @@ public class OutboundGatewayTests {
}).when(context).getBean(anyString());
when(context.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)).thenReturn(true);
IntegrationEvaluationContextFactoryBean integrationEvaluationContextFactoryBean = new IntegrationEvaluationContextFactoryBean();
- integrationEvaluationContextFactoryBean.setApplicationContext(context);
+ integrationEvaluationContextFactoryBean.setBeanFactory(context);
integrationEvaluationContextFactoryBean.afterPropertiesSet();
StandardEvaluationContext evalContext = integrationEvaluationContextFactoryBean.getObject();
when(context.getBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class))
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 d1f7b0b510..39a425b993 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
@@ -18,6 +18,7 @@ package org.springframework.integration.config;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -28,8 +29,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.ConversionService;
@@ -42,62 +42,56 @@ import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.Assert;
/**
+ * {@link FactoryBean} to populate {@link StandardEvaluationContext} instances enhanced with:
+ *
+ * -
+ * a {@link BeanFactoryResolver}.
+ *
+ * -
+ * a {@link TypeConverter} based on the {@link ConversionService} from the application context.
+ *
+ * -
+ * a set of provided {@link PropertyAccessor}s including a default {@link MapAccessor}.
+ *
+ * -
+ * a set of provided SpEL functions.
+ *
+ *
+ *
+ * This factory returns a new instance for each reference singleton - {@link #isSingleton()}
+ * returns false.
+ *
* @author Artem Bilan
* @author Gary Russell
* @since 3.0
*/
public class IntegrationEvaluationContextFactoryBean implements FactoryBean,
- ApplicationContextAware, BeanFactoryAware, InitializingBean {
+ BeanFactoryAware, InitializingBean {
private volatile List propertyAccessors = new ArrayList();
- private TypeConverter typeConverter = new StandardTypeConverter();
-
private volatile Map functions = new LinkedHashMap();
- private ApplicationContext applicationContext;
+ private TypeConverter typeConverter = new StandardTypeConverter();
private BeanFactory beanFactory;
private BeanResolver beanResolver;
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
- @Override
- public void afterPropertiesSet() throws Exception {
- this.beanResolver = new BeanFactoryResolver(this.applicationContext != null ? this.applicationContext : this.beanFactory);
- this.loadDefaultPropertyAccessors(this.propertyAccessors);
- if (this.applicationContext != null) {
- ConversionService conversionService = IntegrationContextUtils.getConversionService(this.applicationContext);
- if (conversionService != null) {
- this.typeConverter = new StandardTypeConverter(conversionService);
- }
- }
- }
-
public void setPropertyAccessors(PropertyAccessor... accessors) {
Assert.noNullElements(accessors, "Cannot have null elements in accessors");
List propertyAccessors = new ArrayList();
loadDefaultPropertyAccessors(propertyAccessors);
- for (PropertyAccessor accessor : accessors) {
- propertyAccessors.add(accessor);
- }
+ Collections.addAll(propertyAccessors, accessors);
this.propertyAccessors = propertyAccessors;
}
- private void loadDefaultPropertyAccessors(List propertyAccessors) {
- propertyAccessors.add(new MapAccessor());
- }
-
public void setFunctions(Map functionsArg) {
Map functions = new LinkedHashMap();
for (Entry function : functionsArg.entrySet()) {
@@ -107,20 +101,60 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean functions) {
+ this.functions.putAll(functions);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (this.propertyAccessors.isEmpty()) {
+ this.loadDefaultPropertyAccessors(this.propertyAccessors);
+ }
+ if (this.beanFactory != null) {
+ this.beanResolver = new BeanFactoryResolver(this.beanFactory);
+ ConversionService conversionService = IntegrationContextUtils.getConversionService(this.beanFactory);
+ if (conversionService != null) {
+ this.typeConverter = new StandardTypeConverter(conversionService);
+ }
+ try {
+ SpelFunctionRegistrar functionRegistrar = this.beanFactory.getBean(SpelFunctionRegistrar.class);
+ if (functionRegistrar != null) {
+ this.addFunctions(functionRegistrar.getFunctions());
+ }
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ //Ignore it.
+ //There is no components within application context.
+ }
+ }
+ }
+
@Override
public StandardEvaluationContext getObject() throws Exception {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
+
+ evaluationContext.setBeanResolver(this.beanResolver);
+ evaluationContext.setTypeConverter(this.typeConverter);
+
for (PropertyAccessor propertyAccessor : this.propertyAccessors) {
evaluationContext.addPropertyAccessor(propertyAccessor);
}
- evaluationContext.setBeanResolver(this.beanResolver);
- evaluationContext.setTypeConverter(this.typeConverter);
+
for (Entry functionEntry : this.functions.entrySet()) {
evaluationContext.registerFunction(functionEntry.getKey(), functionEntry.getValue());
}
+
return evaluationContext;
}
+ private void loadDefaultPropertyAccessors(List propertyAccessors) {
+ propertyAccessors.add(new MapAccessor());
+ }
+
@Override
public Class> getObjectType() {
return StandardEvaluationContext.class;
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/SpelFunctionRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/SpelFunctionRegistrar.java
new file mode 100644
index 0000000000..be98f649be
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/SpelFunctionRegistrar.java
@@ -0,0 +1,83 @@
+/*
+ * 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.lang.reflect.Method;
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ * Utility class that keeps track of a Map of SpEL functions in order to register
+ * them with the "integrationEvaluationContext" upon initialization.
+ *
+ *
+ * The {@link org.springframework.integration.config.xml.SpelFunctionParser}
+ * doesn't register a bean for each <spel-function> within application context.
+ * There is no automatic way to get 'functions' from a parent context
+ * and this class provide a hook to get 'functions' from the parent context.
+ *
+ *
+ * @author Artem Bilan
+ *
+ * @since 3.0
+ */
+class SpelFunctionRegistrar implements ApplicationContextAware, InitializingBean {
+
+ private final Map functions;
+
+ private ApplicationContext applicationContext;
+
+ SpelFunctionRegistrar(Map functions) {
+ this.functions = functions;
+ }
+
+ Map getFunctions() {
+ return functions;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ ApplicationContext parent = this.applicationContext.getParent();
+ if (parent != null) {
+ try {
+ SpelFunctionRegistrar parentFunctionRegistrar = parent.getBean(SpelFunctionRegistrar.class);
+ Map parentFunctions = parentFunctionRegistrar.getFunctions();
+ for (String key : parentFunctions.keySet()) {
+ if(!this.functions.containsKey(key)) {
+ this.functions.put(key, parentFunctions.get(key));
+ }
+ }
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ //Ignore it.
+ //There is no components within parent application context.
+ }
+ }
+ }
+
+}
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 65574d2c66..cacddb2c69 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-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.
@@ -24,6 +24,7 @@ package org.springframework.integration.config.xml;
* @author Oleg Zhurakousky
* @author David Turanski
* @author Gary Russell
+ * @author Artem Bilan
*/
public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHandler {
@@ -74,6 +75,7 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan
registerBeanDefinitionParser("control-bus", new ControlBusParser());
registerBeanDefinitionParser("wire-tap", new GlobalWireTapParser());
registerBeanDefinitionParser("transaction-synchronization-factory", new TransactionSynchronizationFactoryParser());
+ registerBeanDefinitionParser("spel-function", new SpelFunctionParser());
}
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelFunctionParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelFunctionParser.java
new file mode 100644
index 0000000000..b734c1dc65
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/SpelFunctionParser.java
@@ -0,0 +1,93 @@
+/*
+ * 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.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Parser for the <spel-function> element.
+ * Doesn't register a bean within application context, collects 'functions'
+ * within {@link org.springframework.integration.config.SpelFunctionRegistrar} bean.
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+public class SpelFunctionParser implements BeanDefinitionParser {
+
+ private final Map functions = new LinkedHashMap();
+
+ private volatile boolean initialized;
+
+ @Override
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+
+ this.initializeSpelFunctionRegistrarIfNecessary(parserContext);
+
+ String id = element.getAttribute("id");
+ String className = element.getAttribute("class");
+ String signature = element.getAttribute("method");
+
+ Class> clazz = null;
+ try {
+ clazz = ClassUtils.forName(className, parserContext.getReaderContext().getBeanClassLoader());
+ }
+ catch (ClassNotFoundException e) {
+ parserContext.getReaderContext().error(e.getMessage(), element);
+ }
+
+ Method method = BeanUtils.resolveSignature(signature, clazz);
+
+ if (method == null) {
+ parserContext.getReaderContext().error(String.format("No declared method '%s' in class '%s'",
+ signature, className), element);
+ return null;
+ }
+ if (!Modifier.isStatic(method.getModifiers())) {
+ parserContext.getReaderContext().error("SpEL-function method has to be 'static'", element);
+ }
+
+ this.functions.put(id, method);
+
+ return null;
+ }
+
+ private synchronized void initializeSpelFunctionRegistrarIfNecessary(ParserContext parserContext) {
+ if (!this.initialized) {
+ BeanDefinitionBuilder registrarBuilder = BeanDefinitionBuilder
+ .genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SpelFunctionRegistrar")
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
+ .addConstructorArgValue(this.functions);
+ 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 c02468e05b..bde0e093df 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
@@ -3883,6 +3883,40 @@ The list of component name patterns you want to track (e.g., tracked-components
+
+
+
+
+
+ Specifies the name of the SpEL function.
+
+
+
+
+
+
+ The fully qualified class name where to find the static method for SpEL function.
+
+
+
+
+
+
+
+
+
+
+
+ The method signature in the form 'methodName[([arg_list])]',
+ where 'arg_list' is an optional, comma-separated list of fully-qualified
+ type names.
+
+
+
+
+
+
+
+
+
+
+
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 8f0c3e9086..6eb178c80a 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
@@ -19,4 +19,8 @@
+
+
+
+
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 455496787c..1da8524b28 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
@@ -19,14 +19,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -36,6 +40,7 @@ import org.springframework.integration.test.util.TestUtils;
/**
* @author Gary Russell
+ * @author Artem Bilan
* @since 3.0
*
*/
@@ -51,17 +56,34 @@ public class ParentContextTests {
* and parent contexts work.
*/
@Test
- public void testSpelBeanReferencesInChildAndParent() {
- ApplicationContext parent = new ClassPathXmlApplicationContext("ParentContext-context.xml", this.getClass());
+ public void testSpelBeanReferencesInChildAndParent() throws ClassNotFoundException {
+ //To check if 'org.springframework.integration.config.SpelFunctionRegistrar#afterPropertiesSet()'
+ //doesn't throw any Exception
+ AbstractApplicationContext superParent = new GenericApplicationContext();
+ superParent.refresh();
+
+ AbstractApplicationContext parent = new ClassPathXmlApplicationContext(new String[]{"ParentContext-context.xml"},
+ this.getClass(), superParent);
+
+ Class> spelFunctionRegistrarClass = Class.forName("org.springframework.integration.config.SpelFunctionRegistrar");
+ Object parentSpelFunctionRegistrar = parent.getBean(spelFunctionRegistrarClass);
+ assertEquals(2, TestUtils.getPropertyValue(parentSpelFunctionRegistrar, "functions", Map.class).size());
+
assertEquals(2, evalContexts.size());
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(parent);
child.setConfigLocation("org/springframework/integration/expression/ChildContext-context.xml");
child.refresh();
+
+ Object childSpelFunctionRegistrar = child.getBean(spelFunctionRegistrarClass);
+ Map functions = TestUtils.getPropertyValue(childSpelFunctionRegistrar, "functions", Map.class);
+ assertEquals(3, functions.size());
+ assertTrue(functions.containsKey("barParent"));
+
assertEquals(3, evalContexts.size());
assertSame(evalContexts.get(0).getBeanResolver(), evalContexts.get(1).getBeanResolver());
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"));
+ assertSame(parent.getBeanFactory(), TestUtils.getPropertyValue(evalContexts.get(0).getBeanResolver(), "beanFactory"));
+ assertSame(child.getBeanFactory(), TestUtils.getPropertyValue(evalContexts.get(2).getBeanResolver(), "beanFactory"));
// Test transformer expressions
child.getBean("input", MessageChannel.class).send(new GenericMessage("baz"));
@@ -82,4 +104,12 @@ public class ParentContextTests {
}
}
+
+ public static class Bar {
+
+ public static Object bar(Object o) {
+ return o;
+ }
+
+ }
}
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 04fba18278..1e75c00426 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
@@ -15,12 +15,16 @@
+
+
+
+
@@ -36,8 +40,11 @@
+
+
+
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 4c10f192b7..e753c1ab17 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
@@ -47,6 +47,7 @@ import org.springframework.util.Assert;
/**
* @author Mark Fisher
* @author Artem Bilan
+ * @author Gary Russell
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@@ -70,6 +71,9 @@ public class SpelTransformerIntegrationTests {
@Autowired @Qualifier("bar.handler")
private AbstractReplyProducingMessageHandler barHandler;
+ @Autowired
+ private MessageChannel spelFunctionInput;
+
@Test
public void simple() {
@@ -119,6 +123,14 @@ public class SpelTransformerIntegrationTests {
assertEquals("bar", reply.getPayload());
}
+ @Test
+ public void testInt1639SpelFunction() {
+ Message> message = MessageBuilder.withPayload(" foo ").build();
+ this.spelFunctionInput.send(message);
+ Message> result = output.receive(0);
+ assertEquals("foo", result.getPayload());
+ }
+
static class TestBean {
public String getFoo() {
diff --git a/src/reference/docbook/spel.xml b/src/reference/docbook/spel.xml
index c3db2d4f42..809d444a8a 100644
--- a/src/reference/docbook/spel.xml
+++ b/src/reference/docbook/spel.xml
@@ -30,6 +30,9 @@
properties (using getters and setters). This is how the Message headers
and payload properties are accessible.
+
+
+ SpEL Evaluation Context Customization
Starting with Spring Integration 3.0, it is possible to add additional
PropertyAccessors to the SpEL evaluation context.
@@ -58,8 +61,7 @@
]]>
@@ -85,4 +87,63 @@
"#barcalc(payload.myBar)".
+
+ SpEL Functions
+
+ 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.
+ 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.
+
+ For example, assuming we have a useful static method to evaluate XPath:
+
+
+
+ ]]>
+
+ With this sample:
+
+
+ The default IntegrationEvaluationContextFactoryBean bean with id
+ integrationEvaluationContext is registered with the application
+ context.
+
+
+ The <spel-function/> is parsed and added to the functions
+ Map of integrationEvaluationContext as map entry with id as the key
+ and the static Method as the value.
+
+
+ The integrationEvaluationContext factory bean creates a new
+ StandardEvaluationContext instance,
+ and it is configured with the default
+ PropertyAccessors, BeanResolver
+ and the custom function.
+
+
+ That EvaluationContext instance is injected into the
+ ExpressionEvaluatingTransformer bean.
+
+
+
+
+
+ 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.
+
+
+ At this time, PropertyAccessors are not inherited and must be
+ declared as described above in each context.
+
+
+
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml
index 800456cd6e..fa41e2cc1b 100644
--- a/src/reference/docbook/whats-new.xml
+++ b/src/reference/docbook/whats-new.xml
@@ -64,9 +64,9 @@
JMX Support
A new <int-jmx:tree-polling-channel-adapter/> is provided; this
- adapter queries the JMX MBean tree and sends a message with a payload that is the
- graph of objects that matches the query. By default the MBeans are mapped to
- primitives and simple Objects like Map, List and arrays - permitting simple
+ adapter queries the JMX MBean tree and sends a message with a payload that is the
+ graph of objects that matches the query. By default the MBeans are mapped to
+ primitives and simple Objects like Map, List and arrays - permitting simple
transformation, for example, to JSON
.
@@ -82,11 +82,19 @@
Spring Expression Language (SpEL) Configuration
- A new factory bean is provided to allow
+ A new IntegrationEvaluationContextFactoryBean is provided to allow
configuration of custom PropertyAccessors and functions for
use in SpEL expressions throughout the framework. For more information see .
+
+ SpEL Functions Support
+
+ To customize the SpEL EvaluationContext with static
+ Method functions the new <spel-function/>
+ component is introduced. For more information see .
+
+
@@ -303,7 +311,7 @@
All Outbound Gateways (e.g. <jdbc:outbound-gateway/> or <jms:outbound-gateway/>)
are designed for 'request-reply' scenarios. A response is expected from the external service and
- will be published to the reply-channel, or the replyChannel message header.
+ will be published to the reply-channel, or the replyChannel message header.
However, there are some cases where the external system might not always return a
result, e.g. a <jdbc:outbound-gateway/>, when a SELECT ends with an empty ResultSet
or, say, a Web Service is One-Way. An option is therefore needed to configure whether or not a