MethodBasedEvaluationContext reliably exposes varargs
Issue: SPR-14554
(cherry picked from commit fae503d)
			
			
This commit is contained in:
		
							parent
							
								
									aade2d1ec9
								
							
						
					
					
						commit
						4543a28e51
					
				| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright 2002-2015 the original author or authors.
 | 
					 * Copyright 2002-2016 the original author or authors.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -17,8 +17,8 @@
 | 
				
			||||||
package org.springframework.cache.interceptor;
 | 
					package org.springframework.cache.interceptor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.lang.reflect.Method;
 | 
					import java.lang.reflect.Method;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
 | 
					import org.springframework.context.expression.MethodBasedEvaluationContext;
 | 
				
			||||||
import org.springframework.core.ParameterNameDiscoverer;
 | 
					import org.springframework.core.ParameterNameDiscoverer;
 | 
				
			||||||
| 
						 | 
					@ -38,25 +38,27 @@ import org.springframework.core.ParameterNameDiscoverer;
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Costin Leau
 | 
					 * @author Costin Leau
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 * @since 3.1
 | 
					 * @since 3.1
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class CacheEvaluationContext extends MethodBasedEvaluationContext {
 | 
					class CacheEvaluationContext extends MethodBasedEvaluationContext {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final List<String> unavailableVariables;
 | 
						private final Set<String> unavailableVariables = new HashSet<String>(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CacheEvaluationContext(Object rootObject, Method method, Object[] args,
 | 
					 | 
				
			||||||
			ParameterNameDiscoverer paramDiscoverer) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		super(rootObject, method, args, paramDiscoverer);
 | 
						CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
 | 
				
			||||||
		this.unavailableVariables = new ArrayList<String>();
 | 
								ParameterNameDiscoverer parameterNameDiscoverer) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							super(rootObject, method, arguments, parameterNameDiscoverer);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Add the specified variable name as unavailable for that context. Any expression trying
 | 
						 * Add the specified variable name as unavailable for that context.
 | 
				
			||||||
	 * to access this variable should lead to an exception.
 | 
						 * Any expression trying to access this variable should lead to an exception.
 | 
				
			||||||
	 * <p>This permits the validation of expressions that could potentially a variable even
 | 
						 * <p>This permits the validation of expressions that could potentially a
 | 
				
			||||||
	 * when such variable isn't available yet. Any expression trying to use that variable should
 | 
						 * variable even when such variable isn't available yet. Any expression
 | 
				
			||||||
	 * therefore fail to evaluate.
 | 
						 * trying to use that variable should therefore fail to evaluate.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void addUnavailableVariable(String name) {
 | 
						public void addUnavailableVariable(String name) {
 | 
				
			||||||
		this.unavailableVariables.add(name);
 | 
							this.unavailableVariables.add(name);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
package org.springframework.context.expression;
 | 
					package org.springframework.context.expression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.lang.reflect.Method;
 | 
					import java.lang.reflect.Method;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.core.ParameterNameDiscoverer;
 | 
					import org.springframework.core.ParameterNameDiscoverer;
 | 
				
			||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
 | 
					import org.springframework.expression.spel.support.StandardEvaluationContext;
 | 
				
			||||||
| 
						 | 
					@ -34,27 +35,27 @@ import org.springframework.util.ObjectUtils;
 | 
				
			||||||
 * </ol>
 | 
					 * </ol>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 * @author Sergey Podgurskiy
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 * @since 4.2
 | 
					 * @since 4.2
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
					public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final Method method;
 | 
						private final Method method;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final Object[] args;
 | 
						private final Object[] arguments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final ParameterNameDiscoverer paramDiscoverer;
 | 
						private final ParameterNameDiscoverer parameterNameDiscoverer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private boolean paramLoaded = false;
 | 
						private boolean argumentsLoaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] args,
 | 
						public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
 | 
				
			||||||
			ParameterNameDiscoverer paramDiscoverer) {
 | 
								ParameterNameDiscoverer parameterNameDiscoverer) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		super(rootObject);
 | 
							super(rootObject);
 | 
				
			||||||
		this.method = method;
 | 
							this.method = method;
 | 
				
			||||||
		this.args = args;
 | 
							this.arguments = arguments;
 | 
				
			||||||
		this.paramDiscoverer = paramDiscoverer;
 | 
							this.parameterNameDiscoverer = parameterNameDiscoverer;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,9 +65,9 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
				
			||||||
		if (variable != null) {
 | 
							if (variable != null) {
 | 
				
			||||||
			return variable;
 | 
								return variable;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!this.paramLoaded) {
 | 
							if (!this.argumentsLoaded) {
 | 
				
			||||||
			lazyLoadArguments();
 | 
								lazyLoadArguments();
 | 
				
			||||||
			this.paramLoaded = true;
 | 
								this.argumentsLoaded = true;
 | 
				
			||||||
			variable = super.lookupVariable(name);
 | 
								variable = super.lookupVariable(name);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return variable;
 | 
							return variable;
 | 
				
			||||||
| 
						 | 
					@ -76,22 +77,30 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
				
			||||||
	 * Load the param information only when needed.
 | 
						 * Load the param information only when needed.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected void lazyLoadArguments() {
 | 
						protected void lazyLoadArguments() {
 | 
				
			||||||
		// shortcut if no args need to be loaded
 | 
							// Shortcut if no args need to be loaded
 | 
				
			||||||
		if (ObjectUtils.isEmpty(this.args)) {
 | 
							if (ObjectUtils.isEmpty(this.arguments)) {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// save arguments as indexed variables
 | 
							// Expose indexed variables as well as parameter names (if discoverable)
 | 
				
			||||||
		for (int i = 0; i < this.args.length; i++) {
 | 
							String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
 | 
				
			||||||
			setVariable("a" + i, this.args[i]);
 | 
							int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterTypes().length);
 | 
				
			||||||
			setVariable("p" + i, this.args[i]);
 | 
							int argsCount = this.arguments.length;
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method);
 | 
							for (int i = 0; i < paramCount; i++) {
 | 
				
			||||||
		// save parameter names (if discovered)
 | 
								Object value = null;
 | 
				
			||||||
		if (parameterNames != null) {
 | 
								if (argsCount > paramCount && i == paramCount - 1) {
 | 
				
			||||||
			for (int i = 0; i < this.args.length; i++) {
 | 
									// Expose remaining arguments as vararg array for last parameter
 | 
				
			||||||
				setVariable(parameterNames[i], this.args[i]);
 | 
									value = Arrays.copyOfRange(this.arguments, i, argsCount);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else if (argsCount > i) {
 | 
				
			||||||
 | 
									// Actual argument found - otherwise left as null
 | 
				
			||||||
 | 
									value = this.arguments[i];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								setVariable("a" + i, value);
 | 
				
			||||||
 | 
								setVariable("p" + i, value);
 | 
				
			||||||
 | 
								if (paramNames != null) {
 | 
				
			||||||
 | 
									setVariable(paramNames[i], value);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,17 +30,18 @@ import static org.junit.Assert.*;
 | 
				
			||||||
 * Unit tests for {@link MethodBasedEvaluationContext}.
 | 
					 * Unit tests for {@link MethodBasedEvaluationContext}.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 | 
					 * @author Juergen Hoeller
 | 
				
			||||||
 * @author Sergey Podgurskiy
 | 
					 * @author Sergey Podgurskiy
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class MethodBasedEvaluationContextTests {
 | 
					public class MethodBasedEvaluationContextTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
 | 
						private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void simpleArguments() {
 | 
						public void simpleArguments() {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
 | 
				
			||||||
				String.class, Boolean.class);
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, "test", true);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {"test", true});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertEquals("test", context.lookupVariable("a0"));
 | 
							assertEquals("test", context.lookupVariable("a0"));
 | 
				
			||||||
		assertEquals("test", context.lookupVariable("p0"));
 | 
							assertEquals("test", context.lookupVariable("p0"));
 | 
				
			||||||
| 
						 | 
					@ -51,16 +52,21 @@ public class MethodBasedEvaluationContextTests {
 | 
				
			||||||
		assertEquals(true, context.lookupVariable("flag"));
 | 
							assertEquals(true, context.lookupVariable("flag"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertNull(context.lookupVariable("a2"));
 | 
							assertNull(context.lookupVariable("a2"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("p2"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void nullArgument() {
 | 
						public void nullArgument() {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
 | 
				
			||||||
				String.class, Boolean.class);
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assertNull(context.lookupVariable("a0"));
 | 
							assertNull(context.lookupVariable("a0"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p0"));
 | 
							assertNull(context.lookupVariable("p0"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("foo"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a1"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("p1"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("flag"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					@ -68,39 +74,58 @@ public class MethodBasedEvaluationContextTests {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a0"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p0"));
 | 
							assertNull(context.lookupVariable("p0"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("flag"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a1"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p1"));
 | 
							assertNull(context.lookupVariable("p1"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("vararg"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void varArgNull() {
 | 
						public void varArgNull() {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a0"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p0"));
 | 
							assertNull(context.lookupVariable("p0"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("flag"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a1"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p1"));
 | 
							assertNull(context.lookupVariable("p1"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("vararg"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void varArgSingle() {
 | 
						public void varArgSingle() {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, "hello"});
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a0"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p0"));
 | 
							assertNull(context.lookupVariable("p0"));
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("flag"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertEquals("hello", context.lookupVariable("a1"));
 | 
				
			||||||
		assertEquals("hello", context.lookupVariable("p1"));
 | 
							assertEquals("hello", context.lookupVariable("p1"));
 | 
				
			||||||
 | 
							assertEquals("hello", context.lookupVariable("vararg"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void varArgMultiple() {
 | 
						public void varArgMultiple() {
 | 
				
			||||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
							Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
				
			||||||
		MethodBasedEvaluationContext context = createEvaluationContext(method,
 | 
							MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello", "hi");
 | 
				
			||||||
				new Object[] {null, new String[]{"hello", "hi"}});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertNull(context.lookupVariable("a0"));
 | 
				
			||||||
		assertNull(context.lookupVariable("p0"));
 | 
							assertNull(context.lookupVariable("p0"));
 | 
				
			||||||
		assertArrayEquals(new String[]{"hello", "hi"}, (String[]) context.lookupVariable("p1"));
 | 
							assertNull(context.lookupVariable("flag"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("a1"));
 | 
				
			||||||
 | 
							assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("p1"));
 | 
				
			||||||
 | 
							assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("vararg"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) {
 | 
						private MethodBasedEvaluationContext createEvaluationContext(Method method, Object... args) {
 | 
				
			||||||
		return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
 | 
							return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,9 +137,7 @@ public class MethodBasedEvaluationContextTests {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private void hello(Boolean flag, String... vararg){
 | 
							private void hello(Boolean flag, String... vararg){
 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue