1

I hava a http-outbound-gateway that uses dynamic urls with connection and read timeout

<int-http:outbound-gateway request-channel="request"
        reply-channel="response" url-expression="headers.serviceUrl" http-method="POST"
        expected-response-type="java.lang.String" charset="UTF-8" request-factory="httpOutboundRequestFactoryBean" message-converters="messageConverterList" header-mapper="headerMapperBean"/>

<bean id="httpOutboundRequestFactoryBean"
      class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
        <property name="readTimeout" value="5000"/>
        <property name="connectTimeout" value="1000"/>
</bean>

Is it possible to config a specific timeout according to the url defined in headers.serviceUrl?

I searched around and found only this issue.

One workaround could be to use directly RestTemplate and create a RestTemplate instance every time and set dynamically the requestFactory (with timeout) according to the url, but maybe there is a built-in way of doing this with spring integration

thanks in advance!

EDIT: ADD ANSWER

I've added this service activator just before the http-outbound-gateway

public class ThreadLocalSample {

public static ThreadLocal<Integer> serviceTimeout = new ThreadLocal<>();

private static final Logger LOGGER = LoggerFactory.getLogger(ThreadLocalSample.class);

public Message<?> setTimeout(final Message<?> message){
    final int number = (new Random().nextInt(20 - 1 + 1) + 1)*1000;
    serviceTimeout.set(number);
    LOGGER.info("Service timeout thread local: "+number);

    return message;
}

Extended the HttpComponentsClientHttpRequestFactory and override the merge configuration using the threadlocal timeout:

public class MyHttpConnectionFactory extends HttpComponentsClientHttpRequestFactory {

private RequestConfig requestConfig;

private static final Logger LOGGER = LoggerFactory.getLogger(MyHttpConnectionFactory.class);


@Override
protected RequestConfig createRequestConfig(final Object client) {
    this.requestConfig = super.createRequestConfig(client);
    return this.requestConfig;
}

@Override
protected RequestConfig mergeRequestConfig(final RequestConfig clientConfig) {
    if (this.requestConfig == null) {  // nothing to merge
        return clientConfig;
    }

    final RequestConfig.Builder builder = RequestConfig.copy(clientConfig);
    final int connectTimeout = this.requestConfig.getConnectTimeout();
    if (connectTimeout >= 0) {
        builder.setConnectTimeout(connectTimeout);
    }
    final int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout();
    if (connectionRequestTimeout >= 0) {
        builder.setConnectionRequestTimeout(connectionRequestTimeout);
    }
    final int socketTimeout = ThreadLocalSample.serviceTimeout.get();
    LOGGER.info("Service timeout: "+socketTimeout);
    if (socketTimeout >= 0) {
        builder.setSocketTimeout(socketTimeout);
    }
    return builder.build();
}

}

The spring integration flow modified:

<int-http:outbound-gateway request-channel="request"
        reply-channel="response" url-expression="headers.serviceUrl" http-method="POST"
        expected-response-type="java.lang.String" charset="UTF-8" request-factory="httpOutboundRequestFactoryBean" message-converters="messageConverterList" header-mapper="headerMapperBean"/>

<bean id="httpOutboundRequestFactoryBean"
      class="sample.MyHttpConnectionFactory">
        <property name="readTimeout" value="5000"/>
        <property name="connectTimeout" value="1000"/>
</bean>
gllambi
  • 648
  • 1
  • 7
  • 18

1 Answers1

0

Well, there is no and I doubt that we are going to do anything on the matter in the framework. Spring Integration fully relies on the ClientHttpRequestFactory and its contract. Therefore maximum what we can suggest is something custom, extending that your HttpComponentsClientHttpRequestFactory.

If we take a look into source code of this factory, we find something like this:

public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    HttpClient client = getHttpClient();

    HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
    postProcessHttpRequest(httpRequest);
    HttpContext context = createHttpContext(httpMethod, uri);
    if (context == null) {
        context = HttpClientContext.create();
    }

    // Request configuration not set in the context
    if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
        // Use request configuration given by the user, when available
        RequestConfig config = null;
        if (httpRequest instanceof Configurable) {
            config = ((Configurable) httpRequest).getConfig();
        }
        if (config == null) {
            config = createRequestConfig(client);
        }
        if (config != null) {
            context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
        }
    }

So, every time the createRequest() is called it consults with something like HttpContext and/or RequestConfig.

So, as a solution for your requirement I would suggest to deal with ThreadLocal which you populate before the mentioned <int-http:outbound-gateway> and read from that custom createHttpContext() or createRequestConfig().

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Yes, that's only the way to transfer data from one component to another if we don't have an access. The static methods to read/write to/from `ThreadLocal` are savers. See `SecurityContextHolder` in Spring Security with its `ThreadLocalSecurityContextHolderStrategy` implementaiton. And also `RequestContextHolder` in Spring Web. – Artem Bilan Mar 19 '20 at 17:40
  • I'll see them. Thank you very much! – gllambi Mar 19 '20 at 18:36
  • Sorry, forgot :) – gllambi Mar 19 '20 at 19:18
  • Hi Artem, do you have an example of how to use a ThredLocal here? – gllambi Apr 21 '20 at 23:33
  • You create some `public static ThredLocal` property in some class populate its value in the Service Activator before sending a message to the HTTP Outbound Gateway. In the code of custom `createHttpContext()` you get access to that `ThredLocal` variable. The point is that it is really thread-safe and won't affect any other calls to your HTTP gateway. You only need to be sure that population into a `ThredLocal` and its read have to be done in the same thread. – Artem Bilan Apr 22 '20 at 13:13
  • Thanks Artem! I've edited the original post with the solution. Could you please confirm to me, it is what you told me? Sorry to ask for this, but I am new with ThreadLocal usage. – gllambi Apr 23 '20 at 15:30