Recover from error during SpEL MIXED mode compilation
Prior to this commit, SpEL was able to recover from an error that occurred while running a CompiledExpression; however, SpEL was not able to recover from an error that occurred while compiling the expression (such as a java.lang.VerifyError). The latter can occur when multiple threads concurrently change types involved in the expression, such as the concrete type of a custom variable registered via EvaluationContext.setVariable(...), which can result in SpEL generating invalid bytecode. This commit addresses this issue by catching exceptions thrown while compiling an expression and updating the `failedAttempts` and `interpretedCount` counters accordingly. If an exception is caught while operating in SpelCompilerMode.IMMEDIATE mode, the exception will be propagated via a SpelEvaluationException with a new SpelMessage.EXCEPTION_COMPILING_EXPRESSION error category. Closes gh-28043
This commit is contained in:
@@ -76,6 +76,21 @@ class SpelCompilerTests {
|
||||
assertThat(expression.getValue(context)).asInstanceOf(BOOLEAN).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-28043
|
||||
void changingRegisteredVariableTypeDoesNotResultInFailureInMixedMode() {
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.MIXED, null);
|
||||
SpelExpressionParser parser = new SpelExpressionParser(config);
|
||||
Expression sharedExpression = parser.parseExpression("#bean.value");
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
|
||||
Object[] beans = new Object[] {new Bean1(), new Bean2(), new Bean3(), new Bean4()};
|
||||
|
||||
IntStream.rangeClosed(1, 1_000_000).parallel().forEach(count -> {
|
||||
context.setVariable("bean", beans[count % 4]);
|
||||
assertThat(sharedExpression.getValue(context)).asString().startsWith("1");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
static class OrderedComponent implements Ordered {
|
||||
|
||||
@@ -121,4 +136,28 @@ class SpelCompilerTests {
|
||||
boolean hasSomeProperty();
|
||||
}
|
||||
|
||||
public static class Bean1 {
|
||||
public String getValue() {
|
||||
return "11";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bean2 {
|
||||
public Integer getValue() {
|
||||
return 111;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bean3 {
|
||||
public Float getValue() {
|
||||
return 1.23f;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bean4 {
|
||||
public Character getValue() {
|
||||
return '1';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user