/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.spring.rest;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.util.Map;
import java.util.Set;

/**
 * <pre>
 *     支持动态设置超时时间的
 * </pre>
 *
 * @author 田平 create 2022-06-10 16:14:22
 */
public class TimeoutRestTemplate extends RestTemplate {

	public TimeoutRestTemplate(TimeoutHttpClientRequestFactory requestFactory) {
		super(requestFactory);
	}

	/**
	 * Retrieve a representation by doing a GET on the specified URL.
	 * The response (if any) is converted and returned.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 */
	@Nullable
	public <T> T getForObject(int timeOutMs, String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.getForObject(url, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve a representation by doing a GET on the URI template.
	 * The response (if any) is converted and returned.
	 * <p>URI Template variables are expanded using the given map.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @param uriVariables the map containing variables for the URI template
	 * @return the converted object
	 */
	@Nullable
	public <T> T getForObject(int timeOutMs, String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.getForObject(url, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve a representation by doing a GET on the URL .
	 * The response (if any) is converted and returned.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @return the converted object
	 */
	@Nullable
	public <T> T getForObject(int timeOutMs, URI url, Class<T> responseType) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.getForObject(url, responseType);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve an entity by doing a GET on the specified URL.
	 * The response is converted and stored in an {@link ResponseEntity}.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the entity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> getForEntity(int timeOutMs, String url, Class<T> responseType, Object... uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.getForEntity(url, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve a representation by doing a GET on the URI template.
	 * The response is converted and stored in an {@link ResponseEntity}.
	 * <p>URI Template variables are expanded using the given map.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @param uriVariables the map containing variables for the URI template
	 * @return the converted object
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> getForEntity(int timeOutMs, String url, Class<T> responseType, Map<String, ?> uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.getForEntity(url, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve a representation by doing a GET on the URL .
	 * The response is converted and stored in an {@link ResponseEntity}.
	 *
	 * @param url          the URL
	 * @param responseType the type of the return value
	 * @return the converted object
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> getForEntity(int timeOutMs, URI url, Class<T> responseType) throws RestClientException {

		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.getForEntity(url, responseType);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}

	}

	// HEAD

	/**
	 * Retrieve all headers of the resource specified by the URI template.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url          the URL
	 * @param uriVariables the variables to expand the template
	 * @return all HTTP headers of that resource
	 */
	public HttpHeaders headForHeaders(int timeOutMs, String url, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			HttpHeaders result = super.headForHeaders(url, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve all headers of the resource specified by the URI template.
	 * <p>URI Template variables are expanded using the given map.
	 *
	 * @param url          the URL
	 * @param uriVariables the map containing variables for the URI template
	 * @return all HTTP headers of that resource
	 */
	public HttpHeaders headForHeaders(int timeOutMs, String url, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			HttpHeaders result = super.headForHeaders(url, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Retrieve all headers of the resource specified by the URL.
	 *
	 * @param url the URL
	 * @return all HTTP headers of that resource
	 */
	public HttpHeaders headForHeaders(int timeOutMs, URI url) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			HttpHeaders result = super.headForHeaders(url);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// POST

	/**
	 * Create a new resource by POSTing the given object to the URI template, and returns the value of
	 * the {@code Location} header. This header typically indicates where the new resource is stored.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @return the value for the {@code Location} header
	 * @see HttpEntity
	 */
	@Nullable
	public URI postForLocation(int timeOutMs, String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			URI result = super.postForLocation(url, request, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URI template, and returns the value of
	 * the {@code Location} header. This header typically indicates where the new resource is stored.
	 * <p>URI Template variables are expanded using the given map.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @return the value for the {@code Location} header
	 * @see HttpEntity
	 */
	@Nullable
	public URI postForLocation(int timeOutMs, String url, @Nullable Object request, Map<String, ?> uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			URI result = super.postForLocation(url, request, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URL, and returns the value of the
	 * {@code Location} header. This header typically indicates where the new resource is stored.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url     the URL
	 * @param request the Object to be POSTed (may be {@code null})
	 * @return the value for the {@code Location} header
	 * @see HttpEntity
	 */
	@Nullable
	public URI postForLocation(int timeOutMs, URI url, @Nullable Object request) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			URI result = super.postForLocation(url, request);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URI template,
	 * and returns the representation found in the response.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 */
	@Nullable
	public <T> T postForObject(int timeOutMs, String url, @Nullable Object request, Class<T> responseType,
							   Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.postForObject(url, request, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URI template,
	 * and returns the representation found in the response.
	 * <p>URI Template variables are expanded using the given map.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 */
	@Nullable
	public <T> T postForObject(int timeOutMs, String url, @Nullable Object request, Class<T> responseType,
							   Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.postForObject(url, request, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URL,
	 * and returns the representation found in the response.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param responseType the type of the return value
	 * @return the converted object
	 * @see HttpEntity
	 */
	@Nullable
	public <T> T postForObject(int timeOutMs, URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			T result = super.postForObject(url, request, responseType);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URI template,
	 * and returns the response as {@link ResponseEntity}.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> postForEntity(int timeOutMs, String url, @Nullable Object request, Class<T> responseType,
											   Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.postForEntity(url, request, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URI template,
	 * and returns the response as {@link HttpEntity}.
	 * <p>URI Template variables are expanded using the given map.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url          the URL
	 * @param request      the Object to be POSTed (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> postForEntity(int timeOutMs, String url, @Nullable Object request, Class<T> responseType,
											   Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.postForEntity(url, request, responseType, uriVariables);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Create a new resource by POSTing the given object to the URL,
	 * and returns the response as {@link ResponseEntity}.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p>The body of the entity, or {@code request} itself, can be a
	 * {@link org.springframework.util.MultiValueMap MultiValueMap} to create a multipart request.
	 * The values in the {@code MultiValueMap} can be any Object representing the body of the part,
	 * or an {@link HttpEntity HttpEntity} representing a part with body
	 * and headers.
	 *
	 * @param url     the URL
	 * @param request the Object to be POSTed (may be {@code null})
	 * @return the converted object
	 * @see HttpEntity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> postForEntity(int timeOutMs, URI url, @Nullable Object request, Class<T> responseType)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			ResponseEntity<T> result = super.postForEntity(url, request, responseType);
			return result;
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// PUT

	/**
	 * Create or update a resource by PUTting the given object to the URI.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 *
	 * @param url          the URL
	 * @param request      the Object to be PUT (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @see HttpEntity
	 */
	public void put(int timeOutMs, String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.put(url, request, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Creates a new resource by PUTting the given object to URI template.
	 * <p>URI Template variables are expanded using the given map.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 *
	 * @param url          the URL
	 * @param request      the Object to be PUT (may be {@code null})
	 * @param uriVariables the variables to expand the template
	 * @see HttpEntity
	 */
	public void put(int timeOutMs, String url, @Nullable Object request, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.put(url, request, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Creates a new resource by PUTting the given object to URL.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 *
	 * @param url     the URL
	 * @param request the Object to be PUT (may be {@code null})
	 * @see HttpEntity
	 */
	public void put(int timeOutMs, URI url, @Nullable Object request) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.put(url, request);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// PATCH

	/**
	 * Update a resource by PATCHing the given object to the URI template,
	 * and return the representation found in the response.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
	 * You need to use the Apache HttpComponents or OkHttp request factory.</b>
	 *
	 * @param url          the URL
	 * @param request      the object to be PATCHed (may be {@code null})
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 * @see RestTemplate#setRequestFactory
	 * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
	 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
	 * @since 4.3.5
	 */
	@Nullable
	public <T> T patchForObject(int timeOutMs, String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.patchForObject(url, request, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Update a resource by PATCHing the given object to the URI template,
	 * and return the representation found in the response.
	 * <p>URI Template variables are expanded using the given map.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
	 * You need to use the Apache HttpComponents or OkHttp request factory.</b>
	 *
	 * @param url          the URL
	 * @param request      the object to be PATCHed (may be {@code null})
	 * @param responseType the type of the return value
	 * @param uriVariables the variables to expand the template
	 * @return the converted object
	 * @see HttpEntity
	 * @see RestTemplate#setRequestFactory
	 * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
	 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
	 * @since 4.3.5
	 */
	@Nullable
	public <T> T patchForObject(int timeOutMs, String url, @Nullable Object request, Class<T> responseType,
								Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.patchForObject(url, request, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Update a resource by PATCHing the given object to the URL,
	 * and return the representation found in the response.
	 * <p>The {@code request} parameter can be a {@link HttpEntity} in order to
	 * add additional HTTP headers to the request.
	 * <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
	 * You need to use the Apache HttpComponents or OkHttp request factory.</b>
	 *
	 * @param url          the URL
	 * @param request      the object to be PATCHed (may be {@code null})
	 * @param responseType the type of the return value
	 * @return the converted object
	 * @see HttpEntity
	 * @see RestTemplate#setRequestFactory
	 * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
	 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
	 * @since 4.3.5
	 */
	@Nullable
	public <T> T patchForObject(int timeOutMs, URI url, @Nullable Object request, Class<T> responseType)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.patchForObject(url, request, responseType);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// DELETE

	/**
	 * Delete the resources at the specified URI.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url          the URL
	 * @param uriVariables the variables to expand in the template
	 */
	public void delete(int timeOutMs, String url, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.delete(url, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Delete the resources at the specified URI.
	 * <p>URI Template variables are expanded using the given map.
	 *
	 * @param url          the URL
	 * @param uriVariables the variables to expand the template
	 */
	public void delete(int timeOutMs, String url, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.delete(url, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Delete the resources at the specified URL.
	 *
	 * @param url the URL
	 */
	public void delete(int timeOutMs, URI url) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			super.delete(url);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// OPTIONS

	/**
	 * Return the value of the Allow header for the given URI.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url          the URL
	 * @param uriVariables the variables to expand in the template
	 * @return the value of the allow header
	 */
	public Set<HttpMethod> optionsForAllow(int timeOutMs, String url, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.optionsForAllow(url, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Return the value of the Allow header for the given URI.
	 * <p>URI Template variables are expanded using the given map.
	 *
	 * @param url          the URL
	 * @param uriVariables the variables to expand in the template
	 * @return the value of the allow header
	 */
	public Set<HttpMethod> optionsForAllow(int timeOutMs, String url, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.optionsForAllow(url, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Return the value of the Allow header for the given URL.
	 *
	 * @param url the URL
	 * @return the value of the allow header
	 */
	public Set<HttpMethod> optionsForAllow(int timeOutMs, URI url) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.optionsForAllow(url);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// exchange

	/**
	 * Execute the HTTP method to the given URI template, writing the given request entity to the request, and
	 * returns the response as {@link ResponseEntity}.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the request
	 *                      may be {@code null})
	 * @param responseType  the type of the return value
	 * @param uriVariables  the variables to expand in the template
	 * @return the response as entity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  Class<T> responseType, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, writing the given request entity to the request, and
	 * returns the response as {@link ResponseEntity}.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the request
	 *                      (may be {@code null})
	 * @param responseType  the type of the return value
	 * @param uriVariables  the variables to expand in the template
	 * @return the response as entity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, writing the given request entity to the request, and
	 * returns the response as {@link ResponseEntity}.
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the request
	 *                      (may be {@code null})
	 * @param responseType  the type of the return value
	 * @return the response as entity
	 * @since 3.0.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  Class<T> responseType) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, writing the given
	 * request entity to the request, and returns the response as {@link ResponseEntity}.
	 * The given {@link ParameterizedTypeReference} is used to pass generic type information:
	 * <pre class="code">
	 * ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
	 *     new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
	 *
	 * ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
	 *     template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
	 * </pre>
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the
	 *                      request (may be {@code null})
	 * @param responseType  the type of the return value
	 * @param uriVariables  the variables to expand in the template
	 * @return the response as entity
	 * @since 3.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, writing the given
	 * request entity to the request, and returns the response as {@link ResponseEntity}.
	 * The given {@link ParameterizedTypeReference} is used to pass generic type information:
	 * <pre class="code">
	 * ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
	 *     new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
	 *
	 * ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
	 *     template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
	 * </pre>
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the request
	 *                      (may be {@code null})
	 * @param responseType  the type of the return value
	 * @param uriVariables  the variables to expand in the template
	 * @return the response as entity
	 * @since 3.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, writing the given
	 * request entity to the request, and returns the response as {@link ResponseEntity}.
	 * The given {@link ParameterizedTypeReference} is used to pass generic type information:
	 * <pre class="code">
	 * ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
	 *     new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
	 *
	 * ResponseEntity&lt;List&lt;MyBean&gt;&gt; response =
	 *     template.exchange(&quot;https://example.com&quot;,HttpMethod.GET, null, myBean);
	 * </pre>
	 *
	 * @param url           the URL
	 * @param method        the HTTP method (GET, POST, etc)
	 * @param requestEntity the entity (headers and/or body) to write to the request
	 *                      (may be {@code null})
	 * @param responseType  the type of the return value
	 * @return the response as entity
	 * @since 3.2
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
										  ParameterizedTypeReference<T> responseType) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(url, method, requestEntity, responseType);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the request specified in the given {@link RequestEntity} and return
	 * the response as {@link ResponseEntity}. Typically used in combination
	 * with the static builder methods on {@code RequestEntity}, for instance:
	 * <pre class="code">
	 * MyRequest body = ...
	 * RequestEntity request = RequestEntity
	 *     .post(new URI(&quot;https://example.com/foo&quot;))
	 *     .accept(MediaType.APPLICATION_JSON)
	 *     .body(body);
	 * ResponseEntity&lt;MyResponse&gt; response = template.exchange(request, MyResponse.class);
	 * </pre>
	 *
	 * @param requestEntity the entity to write to the request
	 * @param responseType  the type of the return value
	 * @return the response as entity
	 * @since 4.1
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, RequestEntity<?> requestEntity, Class<T> responseType)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(requestEntity, responseType);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the request specified in the given {@link RequestEntity} and return
	 * the response as {@link ResponseEntity}. The given
	 * {@link ParameterizedTypeReference} is used to pass generic type information:
	 * <pre class="code">
	 * MyRequest body = ...
	 * RequestEntity request = RequestEntity
	 *     .post(new URI(&quot;https://example.com/foo&quot;))
	 *     .accept(MediaType.APPLICATION_JSON)
	 *     .body(body);
	 * ParameterizedTypeReference&lt;List&lt;MyResponse&gt;&gt; myBean =
	 *     new ParameterizedTypeReference&lt;List&lt;MyResponse&gt;&gt;() {};
	 * ResponseEntity&lt;List&lt;MyResponse&gt;&gt; response = template.exchange(request, myBean);
	 * </pre>
	 *
	 * @param requestEntity the entity to write to the request
	 * @param responseType  the type of the return value
	 * @return the response as entity
	 * @since 4.1
	 */
	public <T> ResponseEntity<T> exchange(int timeOutMs, RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.exchange(requestEntity, responseType);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	// General execution

	/**
	 * Execute the HTTP method to the given URI template, preparing the request with the
	 * {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
	 * <p>URI Template variables are expanded using the given URI variables, if any.
	 *
	 * @param url               the URL
	 * @param method            the HTTP method (GET, POST, etc)
	 * @param requestCallback   object that prepares the request
	 * @param responseExtractor object that extracts the return value from the response
	 * @param uriVariables      the variables to expand in the template
	 * @return an arbitrary object, as returned by the {@link ResponseExtractor}
	 */
	@Nullable
	public <T> T execute(int timeOutMs, String url, HttpMethod method, @Nullable RequestCallback requestCallback,
						 @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.execute(url, method, requestCallback, responseExtractor, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URI template, preparing the request with the
	 * {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
	 * <p>URI Template variables are expanded using the given URI variables map.
	 *
	 * @param url               the URL
	 * @param method            the HTTP method (GET, POST, etc)
	 * @param requestCallback   object that prepares the request
	 * @param responseExtractor object that extracts the return value from the response
	 * @param uriVariables      the variables to expand in the template
	 * @return an arbitrary object, as returned by the {@link ResponseExtractor}
	 */
	@Nullable
	public <T> T execute(int timeOutMs, String url, HttpMethod method, @Nullable RequestCallback requestCallback,
						 @Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
			throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.execute(url, method, requestCallback, responseExtractor, uriVariables);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

	/**
	 * Execute the HTTP method to the given URL, preparing the request with the
	 * {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
	 *
	 * @param url               the URL
	 * @param method            the HTTP method (GET, POST, etc)
	 * @param requestCallback   object that prepares the request
	 * @param responseExtractor object that extracts the return value from the response
	 * @return an arbitrary object, as returned by the {@link ResponseExtractor}
	 */
	@Nullable
	public <T> T execute(int timeOutMs, URI url, HttpMethod method, @Nullable RequestCallback requestCallback,
						 @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
		TimeoutHttpClientRequestFactory.socketTimeoutMs.set(timeOutMs);
		try {
			return super.execute(url, method, requestCallback, responseExtractor);
		} finally {
			TimeoutHttpClientRequestFactory.socketTimeoutMs.set(null);
		}
	}

}
