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:
parent
ff20a06876
commit
94af2ca06b
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -27,10 +27,11 @@ import java.text.MessageFormat;
|
|||
* <p>When a message is formatted, it will have this kind of form, capturing the prefix
|
||||
* and the error kind:
|
||||
*
|
||||
* <pre class="code">EL1004E: Type cannot be found 'String'</pre>
|
||||
* <pre class="code">EL1005E: Type cannot be found 'String'</pre>
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum SpelMessage {
|
||||
|
@ -255,7 +256,11 @@ public enum SpelMessage {
|
|||
|
||||
/** @since 4.3.17 */
|
||||
FLAWED_PATTERN(Kind.ERROR, 1073,
|
||||
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it");
|
||||
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"),
|
||||
|
||||
/** @since 5.3.16 */
|
||||
EXCEPTION_COMPILING_EXPRESSION(Kind.ERROR, 1074,
|
||||
"An exception occurred while compiling an expression");
|
||||
|
||||
|
||||
private final Kind kind;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -110,7 +110,8 @@ public final class SpelCompiler implements Opcodes {
|
|||
return ReflectionUtils.accessibleConstructor(clazz).newInstance();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to instantiate CompiledExpression", ex);
|
||||
throw new IllegalStateException("Failed to instantiate CompiledExpression for expression: " +
|
||||
expression.toStringAST(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelExpression implements Expression {
|
||||
|
@ -522,17 +523,34 @@ public class SpelExpression implements Expression {
|
|||
// Compiled by another thread before this thread got into the sync block
|
||||
return true;
|
||||
}
|
||||
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
|
||||
compiledAst = compiler.compile(this.ast);
|
||||
if (compiledAst != null) {
|
||||
// Successfully compiled
|
||||
this.compiledAst = compiledAst;
|
||||
return true;
|
||||
try {
|
||||
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
|
||||
compiledAst = compiler.compile(this.ast);
|
||||
if (compiledAst != null) {
|
||||
// Successfully compiled
|
||||
this.compiledAst = compiledAst;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Failed to compile
|
||||
this.failedAttempts.incrementAndGet();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
catch (Exception ex) {
|
||||
// Failed to compile
|
||||
this.failedAttempts.incrementAndGet();
|
||||
return false;
|
||||
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.compiledAst = null;
|
||||
this.interpretedCount.set(0);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_COMPILING_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue