From 757f4ad54677678fee4aa5602ea6c2625095c1d0 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 1 Dec 2016 10:21:51 -0500 Subject: [PATCH] GH-34: Resolve Placeholders in Expressions Further enhancement to #34 --- README.md | 2 ++ ...tationAwareRetryOperationsInterceptor.java | 29 ++++++++++++++----- .../retry/annotation/EnableRetryTests.java | 19 ++++++++++-- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e79aa02..4b98452 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,8 @@ public void service3() { These use the familier Spring SpEL expression syntax (`#{...}`). +Expressions can contain property placeholders such as `#{${max.delay}}` or `#{@exceptionChecker.${retry.method}(#root)}` + - `exceptionExpression` is evaluated against the thrown exception as the `#root` object. - `maxAttemptsExpression` and the `@BackOff` expression attributes are evaluated once, during initialization; there is no root object for the evaluation but they can reference other beans in the context. diff --git a/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java b/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java index daec0f3..406d006 100644 --- a/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java +++ b/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java @@ -31,6 +31,7 @@ import org.springframework.aop.IntroductionInterceptor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; @@ -288,11 +289,11 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn Integer maxAttempts = (Integer) attrs.get("maxAttempts"); String maxAttemptsExpression = (String) attrs.get("maxAttemptsExpression"); if (StringUtils.hasText(maxAttemptsExpression)) { - maxAttempts = PARSER.parseExpression(maxAttemptsExpression, PARSER_CONTEXT).getValue(this.evaluationContext, - Integer.class); + maxAttempts = PARSER.parseExpression(resolve(maxAttemptsExpression), PARSER_CONTEXT) + .getValue(this.evaluationContext, Integer.class); } if (includes.length == 0 && excludes.length == 0) { - SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(exceptionExpression) + SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(resolve(exceptionExpression)) .withBeanFactory(this.beanFactory) : new SimpleRetryPolicy(); simple.setMaxAttempts(maxAttempts); @@ -317,17 +318,17 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn private BackOffPolicy getBackoffPolicy(Backoff backoff) { long min = backoff.delay() == 0 ? backoff.value() : backoff.delay(); if (StringUtils.hasText(backoff.delayExpression())) { - min = PARSER.parseExpression(backoff.delayExpression(), PARSER_CONTEXT).getValue(this.evaluationContext, - Long.class); + min = PARSER.parseExpression(resolve(backoff.delayExpression()), PARSER_CONTEXT) + .getValue(this.evaluationContext, Long.class); } long max = backoff.maxDelay(); if (StringUtils.hasText(backoff.maxDelayExpression())) { - max = PARSER.parseExpression(backoff.maxDelayExpression(), PARSER_CONTEXT).getValue(this.evaluationContext, - Long.class); + max = PARSER.parseExpression(resolve(backoff.maxDelayExpression()), PARSER_CONTEXT) + .getValue(this.evaluationContext, Long.class); } double multiplier = backoff.multiplier(); if (StringUtils.hasText(backoff.multiplierExpression())) { - multiplier = PARSER.parseExpression(backoff.multiplierExpression(), PARSER_CONTEXT) + multiplier = PARSER.parseExpression(resolve(backoff.multiplierExpression()), PARSER_CONTEXT) .getValue(this.evaluationContext, Double.class); } if (multiplier > 0) { @@ -360,4 +361,16 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn return policy; } + /** + * Resolve the specified value if possible. + * + * @see ConfigurableBeanFactory#resolveEmbeddedValue + */ + private String resolve(String value) { + if (this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory) { + return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value); + } + return value; + } + } diff --git a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java index bc9184b..c54a13a 100644 --- a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java +++ b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java @@ -24,6 +24,7 @@ import static org.junit.Assert.fail; import java.lang.reflect.Method; import java.util.Map; +import java.util.Properties; import org.aopalliance.intercept.MethodInterceptor; import org.junit.Test; @@ -33,6 +34,7 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.backoff.Sleeper; import org.springframework.retry.interceptor.RetryInterceptorBuilder; @@ -215,6 +217,18 @@ public class EnableRetryTests { @EnableRetry protected static class TestConfiguration { + @Bean + public static PropertySourcesPlaceholderConfigurer pspc() { + PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); + Properties properties = new Properties(); + properties.setProperty("one", "1"); + properties.setProperty("five", "5"); + properties.setProperty("onePointOne", "1.1"); + properties.setProperty("retryMethod", "shouldRetry"); + pspc.setProperties(properties); + return pspc; + } + @SuppressWarnings("serial") @Bean public Sleeper sleeper() { @@ -447,9 +461,10 @@ public class EnableRetryTests { throw new RuntimeException("this cannot be retried"); } - @Retryable(exceptionExpression="#{@exceptionChecker.shouldRetry(#root)}", + @Retryable(exceptionExpression="#{@exceptionChecker.${retryMethod}(#root)}", maxAttemptsExpression = "#{@integerFiveBean}", - backoff = @Backoff(delayExpression = "#{1}", maxDelayExpression = "#{5}", multiplierExpression = "#{1.1}")) + backoff = @Backoff(delayExpression = "#{${one}}", maxDelayExpression = "#{${five}}", + multiplierExpression = "#{${onePointOne}}")) public void service3() { if (count++ < 8) { throw new RuntimeException();