mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-26 08:59:09 +00:00
Merge branch '7.0.x'
This commit is contained in:
+92
@@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
@@ -47,6 +48,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
import static org.springframework.expression.spel.SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST;
|
||||
import static org.springframework.expression.spel.SpelParserConfiguration.SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME;
|
||||
|
||||
/**
|
||||
* Tests the evaluation of real expressions in a real context.
|
||||
@@ -97,6 +99,96 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
evaluateAndCheckError(parser, expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxOperationsIsConfigurableViaSpelParserConfiguration() {
|
||||
int maxLength = 100;
|
||||
int maxOperations = 10;
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// The following expression contains 10 tracked operations:
|
||||
String expression = "('foo' + 'bar').length >= 6 && {1, 1 + 1, 3}.contains(3)";
|
||||
Expression expr1 = parser.parseExpression(expression);
|
||||
assertThat(expr1.getValue(Boolean.class)).isTrue();
|
||||
|
||||
SpelParserConfiguration configuration =
|
||||
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations);
|
||||
parser = new SpelExpressionParser(configuration);
|
||||
Expression expr2 = parser.parseExpression(expression);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expr2.getValue(Boolean.class))
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.MAX_OPERATIONS_EXCEEDED);
|
||||
assertThat(ex.getInserts()).as("inserts").containsExactly(maxOperations);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxOperationsIsConfigurableViaSpringProperty() {
|
||||
int maxOperations = 10;
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// The following expression contains 10 tracked operations:
|
||||
String expression = "('foo' + 'bar').length >= 6 && {1, 1 + 1, 3}.contains(3)";
|
||||
Expression expr1 = parser.parseExpression(expression);
|
||||
assertThat(expr1.getValue(Boolean.class)).isTrue();
|
||||
|
||||
try {
|
||||
SpringProperties.setProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME, "" + maxOperations);
|
||||
|
||||
parser = new SpelExpressionParser();
|
||||
Expression expr2 = parser.parseExpression(expression);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expr2.getValue(Boolean.class))
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.MAX_OPERATIONS_EXCEEDED);
|
||||
assertThat(ex.getInserts()).as("inserts").containsExactly(maxOperations);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
SpringProperties.setProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxOperationsConfiguredViaSpelParserConfigurationOverridesSpringProperty() {
|
||||
int maxLength = 100;
|
||||
int maxOperations = 10;
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// The following expression contains 10 tracked operations:
|
||||
String expression = "('foo' + 'bar').length >= 6 && {1, 1 + 1, 3}.contains(3)";
|
||||
Expression expr1 = parser.parseExpression(expression);
|
||||
assertThat(expr1.getValue(Boolean.class)).isTrue();
|
||||
|
||||
try {
|
||||
SpringProperties.setProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME, "" + maxOperations / 2);
|
||||
|
||||
// maxOperations + 1 should override maxOperations / 2
|
||||
SpelParserConfiguration configuration =
|
||||
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations + 1);
|
||||
parser = new SpelExpressionParser(configuration);
|
||||
expr1 = parser.parseExpression(expression);
|
||||
assertThat(expr1.getValue(Boolean.class)).isTrue();
|
||||
|
||||
configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations);
|
||||
parser = new SpelExpressionParser(configuration);
|
||||
Expression expr2 = parser.parseExpression(expression);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expr2.getValue(Boolean.class))
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.MAX_OPERATIONS_EXCEEDED);
|
||||
// Should fail due to maxOperations, not maxOperations / 2
|
||||
assertThat(ex.getInserts()).as("inserts").containsExactly(maxOperations);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
SpringProperties.setProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
|
||||
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
|
||||
|
||||
+19
-7
@@ -574,21 +574,26 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
void stringRepeat() {
|
||||
evaluate("'abc' * 0", "", String.class);
|
||||
evaluate("'abc' * 1", "abc", String.class);
|
||||
evaluate("'abc' * 2", "abcabc", String.class);
|
||||
String EMPTY = "";
|
||||
evaluate("'' * 0", EMPTY, String.class);
|
||||
evaluate("'' * 2", EMPTY, String.class);
|
||||
evaluate("'abc' * 0", EMPTY, String.class);
|
||||
|
||||
evaluate("'Abc' * 1", "Abc", String.class);
|
||||
evaluate("'Abc' * 2", "AbcAbc", String.class);
|
||||
evaluate("'Abc' * 3", "AbcAbcAbc", String.class);
|
||||
|
||||
Expression expr = parser.parseExpression("'a' * 256");
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(256);
|
||||
|
||||
// 4 is the position of the '*' (repeat operator)
|
||||
evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4);
|
||||
// 6 is the position of the repeatCount
|
||||
evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 6);
|
||||
|
||||
// Integer overflow: 2 * ((Integer.MAX_VALUE / 2) + 1) --> integer overflow
|
||||
int repeatCount = (Integer.MAX_VALUE / 2) + 1;
|
||||
assertThat(2 * repeatCount).isNegative();
|
||||
// 5 is the position of the '*' (repeat operator)
|
||||
evaluateAndCheckError("'ab' * " + repeatCount, String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 5);
|
||||
// 7 is the position of the repeatCount
|
||||
evaluateAndCheckError("'ab' * " + repeatCount, String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -623,12 +628,19 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
|
||||
|
||||
// Text is too big
|
||||
context.setVariable("text1", createString(maxSize));
|
||||
context.setVariable("text2", createString(maxSize));
|
||||
evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
|
||||
context.setVariable("text1", createString(maxSize + 1));
|
||||
evaluateAndCheckError("#text1 + ''", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("'' + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 3);
|
||||
evaluateAndCheckError("true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 5);
|
||||
|
||||
context.setVariable("text1", createString(maxSize - 1));
|
||||
evaluateAndCheckError("#text1 + 'YZ'", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
|
||||
context.setVariable("text1", createString(maxSize / 2));
|
||||
context.setVariable("text2", createString((maxSize / 2) + 1));
|
||||
evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
|
||||
+12
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.testresources.Inventor;
|
||||
import org.springframework.expression.spel.testresources.Person;
|
||||
import org.springframework.expression.spel.testresources.RecordPerson;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -202,6 +204,16 @@ class PropertyAccessTests extends AbstractExpressionTests {
|
||||
target.setName("p2");
|
||||
assertThat(expr.getValue(context, target)).isEqualTo("p2");
|
||||
|
||||
assertThatSpelEvaluationException()
|
||||
.isThrownBy(() -> parser.parseExpression("nonexistent").getValue(context, target))
|
||||
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE);
|
||||
|
||||
Method getInvalid = ClassUtils.getMethod(Person.class, "getInvalid");
|
||||
assertThat(getInvalid.getReturnType()).isEqualTo(void.class);
|
||||
assertThatSpelEvaluationException()
|
||||
.isThrownBy(() -> parser.parseExpression("invalid").getValue(context, target))
|
||||
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE);
|
||||
|
||||
assertThatSpelEvaluationException()
|
||||
.isThrownBy(() -> parser.parseExpression("name='p3'").getValue(context, target))
|
||||
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.NOT_ASSIGNABLE);
|
||||
|
||||
+57
-17
@@ -30,6 +30,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link InlineList} and {@link InlineMap}.
|
||||
@@ -47,17 +48,35 @@ class InlineCollectionTests {
|
||||
class InlineListTests {
|
||||
|
||||
@Test
|
||||
void listIsCached() {
|
||||
InlineList list = parseList("{1, -2, 3, 4}");
|
||||
assertThat(list.isConstant()).isTrue();
|
||||
assertThat(list.getConstantValue()).isEqualTo(List.of(1, -2, 3, 4));
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
|
||||
void getConstantValue() {
|
||||
InlineList inlineList = parseList("{1, -2, 3, 4}");
|
||||
assertThat(inlineList.isConstant()).isTrue();
|
||||
List list1 = inlineList.getConstantValue();
|
||||
List list2 = inlineList.getValue(expressionState(), List.class);
|
||||
assertThat(list1).containsExactly(1, -2, 3, 4).isSameAs(list2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
void constantListIsCached() {
|
||||
ExpressionState expressionState = expressionState();
|
||||
|
||||
InlineList inlineList = parseList("{1, -2, 3, 4}");
|
||||
assertThat(inlineList.isConstant()).isTrue();
|
||||
List list1 = inlineList.getValue(expressionState, List.class);
|
||||
List list2 = inlineList.getValue(expressionState, List.class);
|
||||
assertThat(list1).containsExactly(1, -2, 3, 4).isSameAs(list2);
|
||||
// Constant inline lists are immutable.
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> list1.add(999));
|
||||
}
|
||||
|
||||
@Test
|
||||
void dynamicListIsNotCached() {
|
||||
InlineList list = parseList("{1, (5 - 3), 3, 4}");
|
||||
assertThat(list.isConstant()).isFalse();
|
||||
assertThat(list.getValue(null)).isEqualTo(List.of(1, 2, 3, 4));
|
||||
assertThat(list.getValue(expressionState())).isEqualTo(List.of(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,11 +129,28 @@ class InlineCollectionTests {
|
||||
class InlineMapTests {
|
||||
|
||||
@Test
|
||||
void mapIsCached() {
|
||||
InlineMap map = parseMap("{1 : 2, 3 : 4}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Integer, Integer> expected = Map.of(1, 2, 3, 4);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
|
||||
void getConstantValue() {
|
||||
InlineMap inlineMap = parseMap("{1 : 2, 3 : 4}");
|
||||
assertThat(inlineMap.isConstant()).isTrue();
|
||||
Map map1 = inlineMap.getConstantValue();
|
||||
Map map2 = inlineMap.getValue(expressionState(), Map.class);
|
||||
assertThat(map1).isEqualTo(Map.of(1, 2, 3, 4)).isSameAs(map2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
void constantMapIsCached() {
|
||||
ExpressionState expressionState = expressionState();
|
||||
|
||||
InlineMap inlineMap = parseMap("{1 : 2, 3 : 4}");
|
||||
assertThat(inlineMap.isConstant()).isTrue();
|
||||
Map map1 = inlineMap.getValue(expressionState, Map.class);
|
||||
Map map2 = inlineMap.getValue(expressionState, Map.class);
|
||||
assertThat(map1).isEqualTo(Map.of(1, 2, 3, 4)).isSameAs(map2);
|
||||
// Constant inline maps are immutable.
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> map1.put(99, "X"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,7 +158,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{-1 : 2, (-2 - 1) : -4}");
|
||||
assertThat(map.isConstant()).isFalse();
|
||||
Map<Integer, Integer> expected = Map.of(-1, 2, -3, -4);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,7 +191,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{-1 : 2, -3 : 4}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Integer, Integer> expected = Map.of(-1, 2, -3, 4);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -163,7 +199,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{1 : -2, 3 : -4}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Integer, Integer> expected = Map.of(1, -2, 3, -4);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,7 +207,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{1L : -2L, 3L : -4L}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Long, Long> expected = Map.of(1L, -2L, 3L, -4L);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,7 +215,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{-1.0f : -2.0f, -3.0f : -4.0f}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Float, Float> expected = Map.of(-1.0f, -2.0f, -3.0f, -4.0f);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -187,7 +223,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{-1.0 : -2.0, -3.0 : -4.0}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Double, Double> expected = Map.of(-1.0, -2.0, -3.0, -4.0);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -195,7 +231,7 @@ class InlineCollectionTests {
|
||||
InlineMap map = parseMap("{-1 : -2, -3 : -4}");
|
||||
assertThat(map.isConstant()).isTrue();
|
||||
Map<Integer, Integer> expected = Map.of(-1, -2, -3, -4);
|
||||
assertThat(map.getValue(null)).isEqualTo(expected);
|
||||
assertThat(map.getValue(expressionState())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private InlineMap parseMap(String s) {
|
||||
@@ -210,6 +246,10 @@ class InlineCollectionTests {
|
||||
return (SpelExpression) parser.parseExpression(s);
|
||||
}
|
||||
|
||||
private static ExpressionState expressionState() {
|
||||
return new ExpressionState(new StandardEvaluationContext());
|
||||
}
|
||||
|
||||
|
||||
private static class NumberHolder {
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
+4
@@ -59,4 +59,8 @@ public class Person {
|
||||
return company;
|
||||
}
|
||||
|
||||
public void getInvalid() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user