title: filter messages that get 200 status from http outbound gateway
body: <p>I have a message flow where I'd like to filter/drop the message if the result of a GET HTTP call (via http:outbound-gateway) is statusCode 200 - i.e case already exists. Put another way if the call gets a 404 (not found) the flow should continue.  Ideally any other status codes or exceptions should go to errorHandler (as now)</p>
  <p>I've tried calling a gateway that uses a chain with http:outbound-gateway with a request-handler-advice-chain, thinking I can trap the 404 and then just let things continue as if it was not an error, then test the statusCode 404 in 'filter'. However hitting a few issues</p>
  <ol>
  <li>The onFailureExpression expression doesn't detect statusCode 404, log shows HttpClientErrorException so presumably exception wasn't caught ?</li>
  <li>What value should I return from onFailureExpression ? null or payload or #payload ? ideally I want the payload before the HTTP call to remain as is.</li>
  <li>Do I need trapException true ?</li>
  <li>Surprised there isn't an easier way ? shame can't just declare on http:outbound-gateway httpStatusCodes that are allowed and treated as normal.  I didn't think an errorChannel and error handler was the right way to go as I want the original flow to continue if 404 status code. So looks odd to put wanted regular behaviour in an error flow, and already have a higher level error handler.</li>
  </ol>
  <pre><code>2020-11-24 10:46:45,060 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for &quot;http://localhost:9095/ccacase/V1/case/challenge/CHG123456789&quot; resulted in 404 (Not Found); invoking error handler
  2020-11-24 10:46:45,134 [main] DEBUG org.springframework.integration.channel.DirectChannel - postSend (sent=true) on channel 'getChallengeCaseChannel', message: GenericMessage [payload=uk.gov.voa.integration.ccacasecheck.json.CdbEvent@624b3544[id=22,eventType=MIGRATE_CHALLENGE_CASE,asstRef=&lt;null&gt;,ccaCaseRef=CHG123456789,assessmentStatus=&lt;null&gt;,settlementCode=&lt;null&gt;,eventDateTime=2020-11-23T12:05,uarn=&lt;null&gt;,sourceActivityId=&lt;null&gt;,activityAction=&lt;null&gt;,additionalProperties={}], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@54f6b629, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@54f6b629, originalPayload=uk.gov.voa.integration.ccacasecheck.json.CdbEvent@624b3544[id=22,eventType=MIGRATE_CHALLENGE_CASE,asstRef=&lt;null&gt;,ccaCaseRef=CHG123456789,assessmentStatus=&lt;null&gt;,settlementCode=&lt;null&gt;,eventDateTime=2020-11-23T12:05,uarn=&lt;null&gt;,sourceActivityId=&lt;null&gt;,activityAction=&lt;null&gt;,additionalProperties={}], message_id=22, history=cdbEventsAMQPChannel,routeEventChain,migrateChallengeCaseEventChannel,migrateChallengeCaseEventChain,statusFlow,getChallengeCaseChannel, id=6d99e890-fc56-74a8-1187-fbc3457685c8, timestamp=1606214804576}]
  2020-11-24 10:46:45,135 [main] DEBUG org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor - SpEL Expression evaluation failed with Exception.org.springframework.web.client.HttpClientErrorException: 404 Not Found
  
  
  Caused by: org.springframework.messaging.MessagingException: failed to transform message headers; nested exception is org.springframework.messaging.MessageHandlingException: Expression evaluation failed: @statusFlow.exchange(#root).headers[http_statusCode]; nested exception is org.springframework.web.client.HttpClientErrorException: 404 Not Found
      at org.springframework.integration.transformer.HeaderEnricher.transform(HeaderEnricher.java:128)
      at org.springframework.integration.transformer.MessageTransformingHandler.handleRequestMessage(MessageTransformingHandler.java:89)
      ... 72 more
  Caused by: org.springframework.messaging.MessageHandlingException: Expression evaluation failed: @statusFlow.exchange(#root).headers[http_statusCode]; nested exception is org.springframework.web.client.HttpClientErrorException: 404 Not Found
      at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:143)
      at org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor.processMessage(ExpressionEvaluatingMessageProcessor.java:72)
      at org.springframework.integration.transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor.processMessage(ExpressionEvaluatingHeaderValueMessageProcessor.java:71)
      at org.springframework.integration.transformer.HeaderEnricher.transform(HeaderEnricher.java:119)
      ... 73 more
  Caused by: org.springframework.web.client.HttpClientErrorException: 404 Not Found
      at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
  </code></pre>
  <pre><code>&lt;int:chain id=&quot;migrateChallengeCaseEventChain&quot; input-channel=&quot;migrateChallengeCaseEventChannel&quot;&gt;
  
          &lt;int:header-enricher&gt;                                
              &lt;int:header name=&quot;caseStatusCode&quot; expression=&quot;@statusFlow.exchange(#root).headers[http_statusCode]&quot; /&gt;    
          &lt;/int:header-enricher&gt;
          
          &lt;!-- to check http_statusCode header and drop any message with found 200 statusCode --&gt;
          &lt;int:filter ref=&quot;status200Filter&quot;/&gt;  
          
          &lt;int:transformer ref=&quot;migrateChallengeCaseTransformer&quot; /&gt;
          &lt;int:transformer ref=&quot;jsonValidationTransformer&quot; /&gt;
          &lt;int:object-to-json-transformer object-mapper=&quot;springJacksonObjectMapper&quot; /&gt;
          &lt;int:header-enricher&gt;
               &lt;int:header name=&quot;contentType&quot; value=&quot;application/json;charset=UTF-8&quot; overwrite=&quot;true&quot;/&gt;
          &lt;/int:header-enricher&gt;  
          &lt;int-amqp:outbound-channel-adapter 
              amqp-template=&quot;amqpTemplate&quot; exchange-name=&quot;cdbEvents.exchange&quot; 
              routing-key=&quot;migrateCdbCcaChallengeCase.request.queue.binding&quot; /&gt;
      &lt;/int:chain&gt;    
      
      &lt;int:gateway id=&quot;statusFlow&quot; default-request-channel=&quot;getChallengeCaseChannel&quot; /&gt;  
      
      &lt;int:channel id=&quot;getChallengeCaseChannel&quot; /&gt;
      
      &lt;!-- Call API to see if Case already exists, 200 status code we want to filter/drop message   --&gt;
      &lt;int:chain id=&quot;getChallengeCaseChain&quot; input-channel=&quot;getChallengeCaseChannel&quot;&gt;
          &lt;int-http:outbound-gateway id=&quot;httpOutboundGatewayChallengeCaseGet&quot;     
                              expected-response-type=&quot;java.lang.String&quot;                                           
                              http-method=&quot;GET&quot;   charset=&quot;UTF-8&quot;
                              extract-request-payload=&quot;true&quot;  
                              request-factory=&quot;httpRequestFactory&quot;                    
                              url=&quot;${mule.case.data.service.uri}/${case.challenge.subpath}/{ccaCaseRefValue}&quot;&gt;
              &lt;int-http:uri-variable name=&quot;ccaCaseRefValue&quot; expression=&quot;headers['originalPayload'].ccaCaseRef&quot;/&gt;  
              &lt;int-http:request-handler-advice-chain&gt;
                      &lt;bean class=&quot;org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice&quot;&gt;
                          &lt;!-- If true, the result of evaluating the onFailureExpression will be returned as the result --&gt;
                          &lt;property name=&quot;returnFailureExpressionResult&quot; value=&quot;true&quot; /&gt;
                          &lt;property name=&quot;onSuccessExpression&quot; value=&quot;payload&quot; /&gt;
                          &lt;!-- If true, any exception will be caught and null returned. Default false --&gt;
                           &lt;property name=&quot;trapException&quot; value=&quot;true&quot; /&gt; 
                          &lt;!-- Set the expression to evaluate against the root message after a failed handler invocation. The exception is available as the variable #exception. Defaults to payload, if failureChannel is configured. --&gt;
                          &lt;property name=&quot;onFailureExpression&quot;  value=&quot;#exception.cause.statusCode == 404 ? payload : #exception&quot;/&gt; 
                          &lt;!-- Set the channel name to which to send the ErrorMessage after evaluating the failure expression. --&gt;
                          &lt;!-- &lt;property name=&quot;failureChannel&quot; ref=&quot;#headers['replyChannel']&quot; /&gt;  --&gt;
                      &lt;/bean&gt;
                  &lt;/int-http:request-handler-advice-chain&gt;
          &lt;/int-http:outbound-gateway&gt;
      
      &lt;/int:chain&gt;    
  </code></pre>
  <p>Solution used was to implement a custom ErrorHandler that extends the default and overrides hasError</p>
  <pre><code>/**
   * To be used with an 'int-http:outbound-gateway' to make a HTTP call and allow a statusCode 404 
   * response to be treated as normal (as well as a 200 statusCode). 
   * Normally a statusCode 404 would throw a 
   * org.springframework.web.client.HttpClientErrorException: 404 Not Found
   * and expect the caller to deal with an error-channel.  
   * This allows processing to continue and subsequently test for a 404 response
   * and route differently 
   *
   */
  public class Allow404StatusCodeResponseHandler extends DefaultResponseErrorHandler  {
  
      /**
       * OVERRIDEN 
       * 
       * Indicate whether the given response has any errors.
       * &lt;p&gt;Implementations will typically inspect the
       * {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
       * @param response the response to inspect
       * @return {@code true} if the response indicates an error; {@code false} otherwise
       * @throws IOException in case of I/O errors
       */
      @Override
      public boolean hasError(ClientHttpResponse response) throws IOException {
          int rawStatusCode = response.getRawStatusCode();
          return (rawStatusCode == 404 || rawStatusCode == 200) ? false : true;  // 404 to be treated as normal and testable downstream
      }
  
  }
  </code></pre>
  <p>Then set this in int-http:outbound-gateway</p>