INT-4217: SpEL Compilable for Required Header Args

JIRA: https://jira.spring.io/browse/INT-4217

Previously, SpEL method calls with required header parameters had the following form:

    #target.messageAndHeader(message, headers['number'] != null ? headers['number'] : T(org.springframework.util.Assert).isTrue(false, 'required header not available: number'))

The SpEL compiler cannot compile this because the else clause of the ternary has no `exitDescriptor` to indicate the type.

Change the expression to use a function for required headers.
Also use an Elvis operator when possible.

Some examples of new expressions:

    #target.optionalAndRequiredHeader(headers['prop'] ?: null, #requiredHeader(headers, 'num'))

    #target.optionalAndRequiredDottedHeader(headers['dot1'] != null ? headers['dot1'].foo : null,
         #requiredHeader(headers, 'dot2').baz

In the second case, we can't use an Elvis because we're accessing a `foo` property of the header.

__cherry-pick to 4.3.x__

- make `ParametersWrapper` `static`
- minor conflicts in imports
- remove the perf test

* Polishing: remove redundant annotation args for their default values usage
This commit is contained in:
Gary Russell
2017-01-27 10:16:31 -05:00
committed by Artem Bilan
parent 1f9d07ae24
commit c5c874f563
2 changed files with 181 additions and 14 deletions

View File

@@ -31,6 +31,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@@ -314,7 +315,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
this.displayString = sb.toString() + "]";
}
private void prepareEvaluationContext() {
private void prepareEvaluationContext() throws Exception {
StandardEvaluationContext context = getEvaluationContext(false);
Class<?> targetType = AopUtils.getTargetClass(this.targetObject);
if (this.method != null) {
@@ -333,6 +334,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
context.registerMethodFilter(targetType, filter);
}
context.setVariable("target", this.targetObject);
context.registerFunction("requiredHeader", ParametersWrapper.class.getDeclaredMethod("getHeader",
Map.class, String.class));
}
private boolean canReturnExpectedType(AnnotatedMethodFilter filter, Class<?> targetType,
@@ -949,12 +952,16 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
+ "disabled or header name is not explicitly provided via @Header annotation.");
String headerRetrievalExpression = "headers['" + headerName + "']";
String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
String fallbackExpression = (annotationAttributes.getBoolean("required")
&& !methodParameter.getParameterType().getName().equals("java.util.Optional"))
? "T(org.springframework.util.Assert).isTrue(false, 'required header not available: "
+ headerName + "')"
: "null";
return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
if (annotationAttributes.getBoolean("required")
&& !methodParameter.getParameterType().equals(Optional.class)) {
return "#requiredHeader(headers, '" + headerName + "')" + relativeExpression;
}
else if (!StringUtils.hasLength(relativeExpression)) {
return headerRetrievalExpression + " ?: null";
}
else {
return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : null";
}
}
private void setExclusiveTargetParameterType(TypeDescriptor targetParameterType,
@@ -999,6 +1006,21 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
this.message = null;
}
/**
* SpEL Function to retrieve a required header.
* @param headers the headers.
* @param header the header name
* @return the header
* @throws IllegalArgumentException if the header does not exist
*/
public static Object getHeader(Map<?, ?> headers, String header) {
Object object = headers.get(header);
if (object == null) {
throw new IllegalArgumentException("required header not available: " + header);
}
return object;
}
public Object getPayload() {
Assert.state(this.payload != null,
"Invalid method parameter for payload: was expecting collection.");