3

I have a http gateway call that's occasionally returning 503 errors. I'd like to configure retry advice around that call, but I don't want to do it for every error, just the 503s.

<int-http:outbound-gateway ... errorHandler="...">
    <int-http:request-handler-advice-chain>
        <int:retry-advice max-attempts="3" />
    </int-http:request-handler-advice-chain>
</int-http:outbound-gateway>

I already have a custom error handler configured that filters statuses (ex: 404) that I don't want to treat as errors, but I don't see an obvious way to control how the advice is applied based on what I can do in the error handler. This question deals with the same issue, but the answer doesn't explain how to control the advice behavior or reissue the request at the error handler level. Is there a specific exception type to throw?

edit: Example based on answer:

<bean id="spelParser" class="org.springframework.expression.spel.standard.SpelExpressionParser" />

<int-http:outbound-gateway ...>
    <int-http:request-handler-advice-chain>
        <bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
            <property name="retryTemplate">
                <bean class="org.springframework.retry.support.RetryTemplate">
                    <property name="retryPolicy">
                        <bean class="org.springframework.retry.policy.ExpressionRetryPolicy">
                            <constructor-arg index="0" type="org.springframework.expression.Expression" value="#{spelParser.parseExpression('cause.statusCode.value() == 503')}" />
                            <property name="maxAttempts" value="3" />
                        </bean>
                    </property>
                    <property name="backOffPolicy">
                        <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
                            <property name="initialInterval" value="1000" />
                            <property name="multiplier" value="2" />
                        </bean>
                    </property>
                </bean>
            </property>
        </bean>
    </int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
Nathan Williams
  • 951
  • 9
  • 12

1 Answers1

3

Well, for the case when you have a HttpServerErrorException but would like to distinguish it by the statusCode from others and don't retry, I would suggest to take a look into the:

 * Subclass of {@link SimpleRetryPolicy} that delegates to super.canRetry() and,
 * if true, further evaluates an expression against the last thrown exception.
 *
 * @author Gary Russell
 * @since 1.2
 *
 */
@SuppressWarnings("serial")
public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFactoryAware {

Where your expression can be like:

expression="statusCode.value() == 503"

UPDATE

Ah! I see. Since ExpressionRetryPolicy uses TemplateParserContext your expression definitely must be like #{statusCode.value() == 503}. But at the same time it is going to be evaluate during bean factory initialization. I suggest you to do something like this:

<bean id="spelParser" class="org.springframework.expression.spel.standard.SpelExpressionParser"/>

and in the ExpressionRetryPolicy bean definition do:

<constructor-arg index="0" type="org.springframework.expression.Expression" 
                 value="#{spelParser.parseExpression('statusCode.value() == 503')}" />

To overcome the collision.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • To customize the policy, you supply the advice as a `` (`RequestHandlerRetryAdvice`), injecting a custom `RetryTemplate` with the custom policy. – Gary Russell Sep 20 '17 at 07:20
  • I think I see how to wire up ExpressionRetryPolicy. (I had to pull in that dependency separately because spring-integration:4.3.12.RELEASE only grabs spring-retry:1.1.x instead of the new 1.2 version where that class was introduced.) However, I'm getting an error at startup because it can't resolve the expression. (`EL1008E: Property or field '$statusCode' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?`) The SPEL I tried is `expression="#{$statusCode.value() == 503}"`. – Nathan Williams Sep 20 '17 at 14:58
  • ??? That's not SpEL. You have to specify `runtime expression`. And I told you already how: `expression="statusCode.value() == 503"`. What you try to do is called `bean factory initialization expression`, but really not runtime. Anyway the `$` symbol is wrong too... – Artem Bilan Sep 20 '17 at 15:07
  • Thanks for the feedback. I started mangling the expression in response to the errors I was getting. I've edited my question to show the XML I'm using. When I give it the expression as you described, I get a runtime error about how the result is string instead of boolean. When I debug, I see that the expression has been parsed as a string literal. Am I completely off track here, or is there something different I need to do with the ExpressionRetryPolicy? – Nathan Williams Sep 20 '17 at 19:02
  • See an UPDATE in my answer. – Artem Bilan Sep 20 '17 at 19:14
  • 1
    It works! I had to insert "cause." at the front of the expression because there's a MessageHandlingException wrapper around the HttpServerErrorException, but I was able to verify that 500s failed immediately and 503s were retried. Thanks Artem and Gary for the explanations. This will no doubt be a lot smoother in a future release of SI. – Nathan Williams Sep 20 '17 at 19:44
  • I raised an issue for Spring Retry: https://github.com/spring-projects/spring-retry/issues/89 – Artem Bilan Sep 20 '17 at 20:31
  • @NathanWilliams how did you verify 500s are failing immediately and 503s are getting retried – Moulesh Kumar Oct 11 '17 at 12:40
  • That's what the `RetryPolicy` is for, @MouleshKumar - `canRetry()` is `boolean` to fail or retry. – Artem Bilan Oct 11 '17 at 13:02
  • I temporarily changed the application that my integration configuration was targeting to return those statuses. Alternatively you could change the URL to an external test site like https://httpstat.us/503 – Nathan Williams Oct 11 '17 at 14:30