INT-4443: Use SimpleEC for uriVariablesExpression
JIRA: https://jira.spring.io/browse/INT-4443 **cherry-pick to 5.0.x, 4.3.x** Polishing; use data binding accessor in test evaluation contexts. Add `.withInstanceMethods()` See https://jira.spring.io/browse/SPR-16588?focusedCommentId=158041&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-158041 * Polishing according PR comments # Conflicts: # build.gradle # src/reference/asciidoc/changes-4.3-5.0.adoc # Conflicts: # build.gradle # spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java # spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java # spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java # spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java # spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-4.3.xsd # spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java # spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java # src/reference/asciidoc/changes-4.2-4.3.adoc # src/reference/asciidoc/http.adoc
This commit is contained in:
committed by
Artem Bilan
parent
ed79a9521c
commit
705d33a30c
@@ -136,7 +136,7 @@ subprojects { subproject ->
|
||||
springSecurityVersion = '4.1.4.RELEASE'
|
||||
springSocialTwitterVersion = '1.1.2.RELEASE'
|
||||
springRetryVersion = '1.1.3.RELEASE'
|
||||
springVersion = project.hasProperty('springVersion') ? project.springVersion : '4.3.14.RELEASE'
|
||||
springVersion = project.hasProperty('springVersion') ? project.springVersion : '4.3.15.BUILD-SNAPSHOT'
|
||||
springWsVersion = '2.3.0.RELEASE'
|
||||
xmlUnitVersion = '1.6'
|
||||
xstreamVersion = '1.4.7'
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2018 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
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.core.convert.ConversionService;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
import org.springframework.integration.expression.SpelPropertyAccessorRegistrar;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract class for integration evaluation context factory beans.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 4.3.15
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractEvaluationContextFactoryBean implements ApplicationContextAware, InitializingBean {
|
||||
|
||||
private Map<String, PropertyAccessor> propertyAccessors = new LinkedHashMap<String, PropertyAccessor>();
|
||||
|
||||
private Map<String, Method> functions = new LinkedHashMap<String, Method>();
|
||||
|
||||
private TypeConverter typeConverter = new StandardTypeConverter();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private boolean initialized;
|
||||
|
||||
protected TypeConverter getTypeConverter() {
|
||||
return this.typeConverter;
|
||||
}
|
||||
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setPropertyAccessors(Map<String, PropertyAccessor> accessors) {
|
||||
Assert.isTrue(!this.initialized, "'propertyAccessors' can't be changed after initialization.");
|
||||
Assert.notNull(accessors, "'accessors' must not be null.");
|
||||
Assert.noNullElements(accessors.values().toArray(), "'accessors' cannot have null values.");
|
||||
this.propertyAccessors = new LinkedHashMap<String, PropertyAccessor>(accessors);
|
||||
}
|
||||
|
||||
public Map<String, PropertyAccessor> getPropertyAccessors() {
|
||||
return this.propertyAccessors;
|
||||
}
|
||||
|
||||
public void setFunctions(Map<String, Method> functionsArg) {
|
||||
Assert.isTrue(!this.initialized, "'functions' can't be changed after initialization.");
|
||||
Assert.notNull(functionsArg, "'functions' must not be null.");
|
||||
Assert.noNullElements(functionsArg.values().toArray(), "'functions' cannot have null values.");
|
||||
this.functions = new LinkedHashMap<String, Method>(functionsArg);
|
||||
}
|
||||
|
||||
public Map<String, Method> getFunctions() {
|
||||
return this.functions;
|
||||
}
|
||||
|
||||
protected void initialize(String beanName) throws Exception {
|
||||
if (this.applicationContext != null) {
|
||||
ConversionService conversionService = IntegrationUtils.getConversionService(getApplicationContext());
|
||||
if (conversionService != null) {
|
||||
this.typeConverter = new StandardTypeConverter(conversionService);
|
||||
}
|
||||
|
||||
Map<String, SpelFunctionFactoryBean> functionFactoryBeanMap = BeanFactoryUtils
|
||||
.beansOfTypeIncludingAncestors(this.applicationContext, SpelFunctionFactoryBean.class);
|
||||
for (SpelFunctionFactoryBean spelFunctionFactoryBean : functionFactoryBeanMap.values()) {
|
||||
if (!getFunctions().containsKey(spelFunctionFactoryBean.getFunctionName())) {
|
||||
getFunctions().put(spelFunctionFactoryBean.getFunctionName(), spelFunctionFactoryBean.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SpelPropertyAccessorRegistrar propertyAccessorRegistrar =
|
||||
this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class);
|
||||
for (Entry<String, PropertyAccessor> entry : propertyAccessorRegistrar.getPropertyAccessors()
|
||||
.entrySet()) {
|
||||
if (!getPropertyAccessors().containsKey(entry.getKey())) {
|
||||
getPropertyAccessors().put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
// There is no 'SpelPropertyAccessorRegistrar' bean in the application context.
|
||||
}
|
||||
|
||||
ApplicationContext parent = this.applicationContext.getParent();
|
||||
|
||||
if (parent != null && parent.containsBean(beanName)) {
|
||||
AbstractEvaluationContextFactoryBean parentFactoryBean = parent.getBean("&" + beanName, getClass());
|
||||
|
||||
for (Entry<String, PropertyAccessor> entry : parentFactoryBean.getPropertyAccessors().entrySet()) {
|
||||
if (!getPropertyAccessors().containsKey(entry.getKey())) {
|
||||
getPropertyAccessors().put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<String, Method> entry : parentFactoryBean.getFunctions().entrySet()) {
|
||||
if (!getFunctions().containsKey(entry.getKey())) {
|
||||
getFunctions().put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,17 +17,9 @@
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
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;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -36,11 +28,8 @@ import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypeLocator;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.expression.SpelPropertyAccessorRegistrar;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -73,107 +62,28 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class IntegrationEvaluationContextFactoryBean implements FactoryBean<StandardEvaluationContext>,
|
||||
ApplicationContextAware, InitializingBean {
|
||||
|
||||
private volatile Map<String, PropertyAccessor> propertyAccessors = new LinkedHashMap<String, PropertyAccessor>();
|
||||
|
||||
private volatile Map<String, Method> functions = new LinkedHashMap<String, Method>();
|
||||
|
||||
private TypeConverter typeConverter = new StandardTypeConverter();
|
||||
public class IntegrationEvaluationContextFactoryBean extends AbstractEvaluationContextFactoryBean
|
||||
implements FactoryBean<StandardEvaluationContext> {
|
||||
|
||||
private volatile TypeLocator typeLocator;
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setPropertyAccessors(Map<String, PropertyAccessor> accessors) {
|
||||
Assert.isTrue(!this.initialized, "'propertyAccessors' can't be changed after initialization.");
|
||||
Assert.notNull(accessors, "'accessors' must not be null.");
|
||||
Assert.noNullElements(accessors.values().toArray(), "'accessors' cannot have null values.");
|
||||
this.propertyAccessors = new LinkedHashMap<String, PropertyAccessor>(accessors);
|
||||
}
|
||||
|
||||
public Map<String, PropertyAccessor> getPropertyAccessors() {
|
||||
return this.propertyAccessors;
|
||||
}
|
||||
|
||||
public void setFunctions(Map<String, Method> functionsArg) {
|
||||
Assert.isTrue(!this.initialized, "'functions' can't be changed after initialization.");
|
||||
Assert.notNull(functionsArg, "'functions' must not be null.");
|
||||
Assert.noNullElements(functionsArg.values().toArray(), "'functions' cannot have null values.");
|
||||
this.functions = new LinkedHashMap<String, Method>(functionsArg);
|
||||
}
|
||||
|
||||
public Map<String, Method> getFunctions() {
|
||||
return this.functions;
|
||||
}
|
||||
|
||||
public void setTypeLocator(TypeLocator typeLocator) {
|
||||
this.typeLocator = typeLocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.applicationContext != null) {
|
||||
this.beanResolver = new BeanFactoryResolver(this.applicationContext);
|
||||
ConversionService conversionService = IntegrationUtils.getConversionService(this.applicationContext);
|
||||
if (conversionService != null) {
|
||||
this.typeConverter = new StandardTypeConverter(conversionService);
|
||||
}
|
||||
|
||||
Map<String, SpelFunctionFactoryBean> functionFactoryBeanMap =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, SpelFunctionFactoryBean.class);
|
||||
for (SpelFunctionFactoryBean spelFunctionFactoryBean : functionFactoryBeanMap.values()) {
|
||||
if (!this.functions.containsKey(spelFunctionFactoryBean.getFunctionName())) {
|
||||
this.functions.put(spelFunctionFactoryBean.getFunctionName(), spelFunctionFactoryBean.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SpelPropertyAccessorRegistrar propertyAccessorRegistrar =
|
||||
this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class);
|
||||
for (Entry<String, PropertyAccessor> entry : propertyAccessorRegistrar.getPropertyAccessors().entrySet()) {
|
||||
if (!this.propertyAccessors.containsKey(entry.getKey())) {
|
||||
this.propertyAccessors.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
// There is no 'SpelPropertyAccessorRegistrar' bean in the application context.
|
||||
}
|
||||
|
||||
ApplicationContext parent = this.applicationContext.getParent();
|
||||
|
||||
if (parent != null && parent.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) {
|
||||
IntegrationEvaluationContextFactoryBean parentFactoryBean =
|
||||
parent.getBean("&" + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME,
|
||||
IntegrationEvaluationContextFactoryBean.class);
|
||||
|
||||
for (Entry<String, PropertyAccessor> entry : parentFactoryBean.getPropertyAccessors().entrySet()) {
|
||||
if (!this.propertyAccessors.containsKey(entry.getKey())) {
|
||||
this.propertyAccessors.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<String, Method> entry : parentFactoryBean.getFunctions().entrySet()) {
|
||||
if (!this.functions.containsKey(entry.getKey())) {
|
||||
this.functions.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getApplicationContext() != null) {
|
||||
this.beanResolver = new BeanFactoryResolver(getApplicationContext());
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
initialize(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,15 +94,15 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
|
||||
}
|
||||
|
||||
evaluationContext.setBeanResolver(this.beanResolver);
|
||||
evaluationContext.setTypeConverter(this.typeConverter);
|
||||
evaluationContext.setTypeConverter(getTypeConverter());
|
||||
|
||||
for (PropertyAccessor propertyAccessor : this.propertyAccessors.values()) {
|
||||
for (PropertyAccessor propertyAccessor : getPropertyAccessors().values()) {
|
||||
evaluationContext.addPropertyAccessor(propertyAccessor);
|
||||
}
|
||||
|
||||
evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||
|
||||
for (Entry<String, Method> functionEntry : this.functions.entrySet()) {
|
||||
for (Entry<String, Method> functionEntry : getFunctions().entrySet()) {
|
||||
evaluationContext.registerFunction(functionEntry.getKey(), functionEntry.getValue());
|
||||
}
|
||||
|
||||
@@ -204,9 +114,4 @@ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<Stan
|
||||
return StandardEvaluationContext.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -194,6 +194,17 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
|
||||
".expression.IntegrationEvaluationContextAwareBeanPostProcessor");
|
||||
BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, registry);
|
||||
}
|
||||
if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(IntegrationSimpleEvaluationContextFactoryBean.class);
|
||||
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
BeanDefinitionHolder integrationEvaluationContextHolder =
|
||||
new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(),
|
||||
IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME);
|
||||
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, registry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2018 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.Collection;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.expression.SpelPropertyAccessorRegistrar;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@link FactoryBean} to populate {@link SimpleEvaluationContext} instances enhanced with:
|
||||
* <ul>
|
||||
* <li>
|
||||
* a {@link TypeConverter} based on the {@link ConversionService} from the application context.
|
||||
* </li>
|
||||
* <li>
|
||||
* a set of provided {@link PropertyAccessor}s including a default {@link MapAccessor}.
|
||||
* </li>
|
||||
* <li>
|
||||
* a set of provided SpEL functions.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <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
|
||||
*
|
||||
* @since 4.3.15
|
||||
*/
|
||||
public class IntegrationSimpleEvaluationContextFactoryBean extends AbstractEvaluationContextFactoryBean
|
||||
implements FactoryBean<SimpleEvaluationContext> {
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
initialize(IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleEvaluationContext getObject() throws Exception {
|
||||
Collection<PropertyAccessor> accessors = getPropertyAccessors().values();
|
||||
PropertyAccessor[] accessorArray = accessors.toArray(new PropertyAccessor[accessors.size() + 2]);
|
||||
accessorArray[accessors.size()] = new MapAccessor();
|
||||
accessorArray[accessors.size() + 1] = DataBindingPropertyAccessor.forReadOnlyAccess();
|
||||
SimpleEvaluationContext evaluationContext =
|
||||
SimpleEvaluationContext.forPropertyAccessors(accessorArray)
|
||||
.withTypeConverter(getTypeConverter())
|
||||
.withInstanceMethods()
|
||||
.build();
|
||||
for (Entry<String, Method> functionEntry : getFunctions().entrySet()) {
|
||||
evaluationContext.setVariable(functionEntry.getKey(), functionEntry.getValue());
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return SimpleEvaluationContext.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.context;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
@@ -48,6 +49,8 @@ public abstract class IntegrationContextUtils {
|
||||
|
||||
public static final String INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME = "integrationEvaluationContext";
|
||||
|
||||
public static final String INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME = "integrationSimpleEvaluationContext";
|
||||
|
||||
public static final String INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME = "integrationHeaderChannelRegistry";
|
||||
|
||||
public static final String INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME = "integrationGlobalProperties";
|
||||
@@ -120,12 +123,24 @@ public abstract class IntegrationContextUtils {
|
||||
|
||||
/**
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
* @return the instance of {@link StandardEvaluationContext} bean whose name is "integrationEvaluationContext" .
|
||||
* @return the instance of {@link StandardEvaluationContext} bean whose name is
|
||||
* {@value #INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME}.
|
||||
*/
|
||||
public static StandardEvaluationContext getEvaluationContext(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
* @return the instance of {@link SimpleEvaluationContext} bean whose name is
|
||||
* {@value #INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME}.
|
||||
* @since 4.3.15
|
||||
*/
|
||||
public static SimpleEvaluationContext getSimpleEvaluationContext(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME,
|
||||
SimpleEvaluationContext.class);
|
||||
}
|
||||
|
||||
private static <T> T getBeanOfType(BeanFactory beanFactory, String beanName, Class<T> type) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
if (!beanFactory.containsBean(beanName)) {
|
||||
@@ -149,7 +164,8 @@ public abstract class IntegrationContextUtils {
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(IntegrationProperties.defaults());
|
||||
if (beanFactory != null) {
|
||||
Properties userProperties = getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
|
||||
Properties userProperties =
|
||||
getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
|
||||
if (userProperties != null) {
|
||||
properties.putAll(userProperties);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -16,18 +16,22 @@
|
||||
|
||||
package org.springframework.integration.expression;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext.Builder;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Utility class with static methods for helping with establishing environments for
|
||||
* SpEL expressions.
|
||||
@@ -40,32 +44,22 @@ import org.apache.commons.logging.LogFactory;
|
||||
public abstract class ExpressionUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ExpressionUtils.class);
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property and the supplied {@link ConversionService} in its
|
||||
* conversionService property.
|
||||
* @param conversionService the conversion service.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
private static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService,
|
||||
BeanFactory beanFactory) {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||
if (conversionService != null) {
|
||||
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a context with no BeanFactory, usually in tests.
|
||||
* @return The evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext() {
|
||||
return doCreateContext(null);
|
||||
return (StandardEvaluationContext) doCreateContext(null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a context with no BeanFactory, usually in tests.
|
||||
* @return The evaluation context.
|
||||
* @since 4.3.15
|
||||
*/
|
||||
public static SimpleEvaluationContext createSimpleEvaluationContext() {
|
||||
return (SimpleEvaluationContext) doCreateContext(null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,22 +72,73 @@ public abstract class ExpressionUtils {
|
||||
if (beanFactory == null) {
|
||||
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
|
||||
}
|
||||
return doCreateContext(beanFactory);
|
||||
return (StandardEvaluationContext) doCreateContext(beanFactory, false);
|
||||
}
|
||||
|
||||
private static StandardEvaluationContext doCreateContext(BeanFactory beanFactory) {
|
||||
/**
|
||||
* Obtains the context from the beanFactory if not null; emits a warning if the beanFactory
|
||||
* is null.
|
||||
* @param beanFactory The bean factory.
|
||||
* @return The evaluation context.
|
||||
* @since 4.3.15
|
||||
*/
|
||||
public static SimpleEvaluationContext createSimpleEvaluationContext(BeanFactory beanFactory) {
|
||||
if (beanFactory == null) {
|
||||
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanFactory"));
|
||||
}
|
||||
return (SimpleEvaluationContext) doCreateContext(beanFactory, true);
|
||||
}
|
||||
|
||||
private static EvaluationContext doCreateContext(BeanFactory beanFactory, boolean simple) {
|
||||
ConversionService conversionService = null;
|
||||
StandardEvaluationContext evaluationContext = null;
|
||||
EvaluationContext evaluationContext = null;
|
||||
if (beanFactory != null) {
|
||||
evaluationContext = IntegrationContextUtils.getEvaluationContext(beanFactory);
|
||||
evaluationContext =
|
||||
simple
|
||||
? IntegrationContextUtils.getSimpleEvaluationContext(beanFactory)
|
||||
: IntegrationContextUtils.getEvaluationContext(beanFactory);
|
||||
}
|
||||
if (evaluationContext == null) {
|
||||
if (beanFactory != null) {
|
||||
conversionService = IntegrationUtils.getConversionService(beanFactory);
|
||||
}
|
||||
evaluationContext = createStandardEvaluationContext(conversionService, beanFactory);
|
||||
evaluationContext = createEvaluationContext(conversionService, beanFactory, simple);
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property and the supplied {@link ConversionService} in its
|
||||
* conversionService property.
|
||||
* @param conversionService the conversion service.
|
||||
* @param beanFactory the bean factory.
|
||||
* @param simple true if simple.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
private static EvaluationContext createEvaluationContext(ConversionService conversionService,
|
||||
BeanFactory beanFactory, boolean simple) {
|
||||
|
||||
if (simple) {
|
||||
Builder ecBuilder = SimpleEvaluationContext.forPropertyAccessors(
|
||||
new MapAccessor(), DataBindingPropertyAccessor.forReadOnlyAccess())
|
||||
.withInstanceMethods();
|
||||
if (conversionService != null) {
|
||||
ecBuilder.withConversionService(conversionService);
|
||||
}
|
||||
return ecBuilder.build();
|
||||
}
|
||||
else {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||
if (conversionService != null) {
|
||||
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
@@ -695,7 +696,7 @@ public class EnableIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testIntegrationEvaluationContextCustomization() {
|
||||
EvaluationContext evaluationContext = this.context.getBean(EvaluationContext.class);
|
||||
EvaluationContext evaluationContext = this.context.getBean(StandardEvaluationContext.class);
|
||||
List<?> propertyAccessors = TestUtils.getPropertyValue(evaluationContext, "propertyAccessors", List.class);
|
||||
assertEquals(4, propertyAccessors.size());
|
||||
assertThat(propertyAccessors.get(0), instanceOf(JsonPropertyAccessor.class));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -76,6 +76,7 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "trusted-spel");
|
||||
HttpAdapterParsingUtils.setExpectedResponseOrExpression(element, parserContext, builder);
|
||||
HttpAdapterParsingUtils.configureUriVariableExpressions(builder, parserContext, element);
|
||||
return builder.getBeanDefinition();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -82,6 +82,7 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-request-payload", "extractPayload");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "trusted-spel");
|
||||
|
||||
HttpAdapterParsingUtils.setExpectedResponseOrExpression(element, parserContext, builder);
|
||||
|
||||
|
||||
@@ -30,8 +30,10 @@ import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -87,7 +89,11 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private volatile StandardEvaluationContext evaluationContext;
|
||||
private StandardEvaluationContext evaluationContext;
|
||||
|
||||
private SimpleEvaluationContext simpleEvaluationContext;
|
||||
|
||||
private boolean trustedSpel;
|
||||
|
||||
private final Expression uriExpression;
|
||||
|
||||
@@ -112,7 +118,6 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
private volatile Expression uriVariablesExpression;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a handler that will send requests to the provided URI.
|
||||
*
|
||||
@@ -338,6 +343,17 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
this.transferCookies = transferCookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you trust the source of SpEL expressions used to evaluate URI
|
||||
* variables. Default is false, which means a {@link SimpleEvaluationContext} is used
|
||||
* for evaluating such expressions, which restricts the use of some SpEL capabilities.
|
||||
* @param trustedSpel true to trust.
|
||||
* @since 4.3.15.
|
||||
*/
|
||||
public void setTrustedSpel(boolean trustedSpel) {
|
||||
this.trustedSpel = trustedSpel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (this.expectReply ? "http:outbound-gateway" : "http:outbound-channel-adapter");
|
||||
@@ -346,6 +362,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
@Override
|
||||
protected void doInit() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
this.simpleEvaluationContext = ExpressionUtils.createSimpleEvaluationContext(this.getBeanFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -562,7 +579,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
* If all keys and values are Strings, we'll consider the Map to be form data.
|
||||
*/
|
||||
private boolean isFormData(Map<Object, ?> map) {
|
||||
for (Object key : map.keySet()) {
|
||||
for (Object key : map.keySet()) {
|
||||
if (!(key instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
@@ -599,7 +616,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
|| expectedResponseType instanceof String
|
||||
|| expectedResponseType instanceof ParameterizedTypeReference,
|
||||
"'expectedResponseType' can be an instance of 'Class<?>', 'String' or 'ParameterizedTypeReference<?>'; "
|
||||
+ "evaluation resulted in a" + expectedResponseType.getClass() + ".");
|
||||
+ "evaluation resulted in a" + expectedResponseType.getClass() + ".");
|
||||
if (expectedResponseType instanceof String && StringUtils.hasText((String) expectedResponseType)) {
|
||||
expectedResponseType = ClassUtils.forName((String) expectedResponseType,
|
||||
getApplicationContext().getClassLoader());
|
||||
@@ -612,20 +629,24 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
private Map<String, ?> determineUriVariables(Message<?> requestMessage) {
|
||||
Map<String, ?> expressions;
|
||||
|
||||
EvaluationContext evaluationContextToUse = this.evaluationContext;
|
||||
if (this.uriVariablesExpression != null) {
|
||||
Object expressionsObject = this.uriVariablesExpression.getValue(this.evaluationContext, requestMessage);
|
||||
Assert.state(expressionsObject instanceof Map,
|
||||
"The 'uriVariablesExpression' evaluation must result in a 'Map'.");
|
||||
expressions = (Map<String, ?>) expressionsObject;
|
||||
if (!this.trustedSpel) {
|
||||
evaluationContextToUse = this.simpleEvaluationContext;
|
||||
}
|
||||
}
|
||||
else {
|
||||
expressions = this.uriVariableExpressions;
|
||||
}
|
||||
|
||||
return ExpressionEvalMap.from(expressions)
|
||||
.usingEvaluationContext(this.evaluationContext)
|
||||
.withRoot(requestMessage)
|
||||
.build();
|
||||
.usingEvaluationContext(evaluationContextToUse)
|
||||
.withRoot(requestMessage)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -397,6 +397,18 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="trusted-spel">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Set to 'true' if you trust SpEL expressions that might be evaluated to generate
|
||||
URI variables.
|
||||
The default value is 'false'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
@@ -487,6 +499,18 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
|
||||
<xsd:attribute name="trusted-spel">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Set to 'true' if you trust SpEL expressions that might be evaluated to generate
|
||||
URI variables.
|
||||
The default value is 'false'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<si:chain input-channel="httpOutboundChannelAdapterWithinChain">
|
||||
<outbound-channel-adapter url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"/>
|
||||
<si:chain id="chain" input-channel="httpOutboundChannelAdapterWithinChain">
|
||||
<outbound-channel-adapter id="adapter" url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"
|
||||
trusted-spel="true" />
|
||||
</si:chain>
|
||||
|
||||
<beans:bean id="restTemplate" class="org.mockito.Mockito" factory-method="spy">
|
||||
@@ -22,6 +23,7 @@
|
||||
<outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
|
||||
rest-template="restTemplate"
|
||||
encode-uri="false"
|
||||
trusted-spel="true"
|
||||
expected-response-type-expression="T (org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests).testParameterizedTypeReference()">
|
||||
<uri-variable name="param" expression="T(java.net.URLEncoder).encode('http Outbound Gateway Within Chain', 'UTF-8')"/>
|
||||
</outbound-gateway>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package org.springframework.integration.http.outbound;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
@@ -708,6 +710,9 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
channel.send(MessageBuilder.withPayload("test").build());
|
||||
Mockito.verify(restTemplate).exchange(Mockito.eq(new URI("http://localhost/test1/%2f")),
|
||||
Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.<Class<Object>>eq(null));
|
||||
HttpRequestExecutingMessageHandler handler = ctx.getBean("chain$child.adapter.handler",
|
||||
HttpRequestExecutingMessageHandler.class);
|
||||
assertThat(TestUtils.getPropertyValue(handler, "trustedSpel"), equalTo(Boolean.TRUE));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -134,13 +134,15 @@ public class UriVariableExpressionTests {
|
||||
handler.setUriVariablesExpression(new SpelExpressionParser().parseExpression("headers.uriVariables"));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
Map<String, String> expressions = new HashMap<String, String>();
|
||||
Map<String, Object> expressions = new HashMap<String, Object>();
|
||||
expressions.put("foo", "bar");
|
||||
|
||||
Map<String, ?> expressionsMap = ExpressionEvalMap.from(expressions).usingSimpleCallback().build();
|
||||
|
||||
try {
|
||||
handler.handleMessage(MessageBuilder.withPayload("test").setHeader("uriVariables", expressionsMap).build());
|
||||
handler.handleMessage(MessageBuilder.withPayload("test")
|
||||
.setHeader("uriVariables", expressionsMap)
|
||||
.build());
|
||||
fail("Exception expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -148,6 +150,42 @@ public class UriVariableExpressionTests {
|
||||
}
|
||||
|
||||
assertEquals("http://test/bar", uriHolder.get().toString());
|
||||
|
||||
expressions.put("foo", new SpelExpressionParser().parseExpression("'bar'.toUpperCase()"));
|
||||
try {
|
||||
handler.handleMessage(MessageBuilder.withPayload("test")
|
||||
.setHeader("uriVariables", expressions)
|
||||
.build());
|
||||
fail("Exception expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("intentional", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
assertEquals("http://test/BAR", uriHolder.get().toString());
|
||||
|
||||
expressions.put("foo", new SpelExpressionParser().parseExpression("T(Integer).valueOf('42')"));
|
||||
try {
|
||||
handler.handleMessage(MessageBuilder.withPayload("test")
|
||||
.setHeader("uriVariables", expressions)
|
||||
.build());
|
||||
fail("Exception expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getCause().getMessage(), containsString("Type cannot be found"));
|
||||
}
|
||||
|
||||
handler.setTrustedSpel(true);
|
||||
try {
|
||||
handler.handleMessage(MessageBuilder.withPayload("test")
|
||||
.setHeader("uriVariables", expressions)
|
||||
.build());
|
||||
fail("Exception expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("intentional", e.getCause().getMessage());
|
||||
}
|
||||
assertEquals("http://test/42", uriHolder.get().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -523,6 +523,7 @@ Changes in Spring 3.1 can cause some issues with escaped characters, such as '?'
|
||||
For this reason, it is recommended that if you wish to generate the URL entirely at runtime, you use the 'url-expression' attribute.
|
||||
=====
|
||||
|
||||
[[mapping-uri-variables]]
|
||||
==== Mapping URI Variables
|
||||
|
||||
If your URL contains URI variables, you can map them using the `uri-variable` sub-element.
|
||||
@@ -581,6 +582,53 @@ NOTE: The `uri-variables-expression` must evaluate to a `Map`.
|
||||
The values of the Map must be instances of `String` or `Expression`.
|
||||
This Map is provided to an `ExpressionEvalMap` for further resolution of URI variable placeholders using those expressions in the context of the outbound `Message`.
|
||||
|
||||
IMPORTANT
|
||||
====
|
||||
The `uriVariablesExpression` property provides a very powerful mechanism for evaluating URI variables.
|
||||
It is anticipated that simple expressions like the example above will be used.
|
||||
However, you could also configure something like this `"@uriVariablesBean.populate(#root)"` with an expression in the returned map being `variables.put("foo", EXPRESSION_PARSER.parseExpression(message.getHeaders().get("bar", String.class)));`, where the expression is dynamically provided in the message header `bar`.
|
||||
Since the header may come from an untrusted source, the HTTP outbound endpoints use a `SimpleEvaluationContext` when evaluating these expressions; allowing only a subset of SpEL features to be used.
|
||||
If you trust your message sources and wish to use the restricted SpEL constructs, set the `trustedSpel` property of the outbound endpoint to `true`.
|
||||
====
|
||||
|
||||
Scenarios when we need to supply a dynamic set of URI variables on per message basis can be achieved with the custom `url-expression` and some utilities for building and encoding URL parameters:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
url-expression="T(org.springframework.web.util.UriComponentsBuilder)
|
||||
.fromHttpUrl('http://HOST:PORT/PATH')
|
||||
.queryParams(payload)
|
||||
.build()
|
||||
.toUri()"
|
||||
----
|
||||
|
||||
where `queryParams()` expects a `MultiValueMap<String, String>` as an argument, so a real set of URL query parameters can be build in advance, before performing request.
|
||||
|
||||
The whole `queryString` can also be presented as an uri variable:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-http:outbound-gateway id="proxyGateway" request-channel="testChannel"
|
||||
url="http://testServer/test?{queryString}">
|
||||
<int-http:uri-variable name="queryString" expression="'a=A&b=B'"/>
|
||||
</int-http:outbound-gateway>
|
||||
----
|
||||
|
||||
In this case the URL encoding must be provided manually.
|
||||
For example the `org.apache.http.client.utils.URLEncodedUtils#format()` can be used for this purpose.
|
||||
A mentioned, manually built, `MultiValueMap<String, String>` can be converted to the the `List<NameValuePair>` `format()` method argument using this Java Streams snippet:
|
||||
[source,java]
|
||||
----
|
||||
List<NameValuePair> nameValuePairs =
|
||||
params.entrySet()
|
||||
.stream()
|
||||
.flatMap(e -> e
|
||||
.getValue()
|
||||
.stream()
|
||||
.map(v -> new BasicNameValuePair(e.getKey(), v)))
|
||||
.collect(Collectors.toList());
|
||||
----
|
||||
|
||||
==== Controlling URI Encoding
|
||||
|
||||
By default, the URL string is encoded (see http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html[UriComponentsBuilder]) to the URI object before sending the request.
|
||||
|
||||
Reference in New Issue
Block a user