io.github.danielliu1123.httpexchange.HttpExchangeProperties Maven / Gradle / Ivy
package io.github.danielliu1123.httpexchange;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toMap;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Http Clients Configuration Properties.
*
* @author Freeman
*/
@Data
@ConfigurationProperties(HttpExchangeProperties.PREFIX)
public class HttpExchangeProperties implements InitializingBean {
public static final String PREFIX = "http-exchange";
/**
* Whether to enable http exchange autoconfiguration, default {@code true}.
*/
private boolean enabled = true;
/**
* Base packages to scan, use {@link EnableExchangeClients#basePackages} first if configured.
*/
private Set basePackages = new LinkedHashSet<>();
/**
* Exchange client interfaces to register as beans, use {@link EnableExchangeClients#clients} first if configured.
*
* @since 3.2.0
*/
private Set> clients = new LinkedHashSet<>();
/**
* Default base url, 'http' scheme can be omitted.
*
* If loadbalancer is enabled, this value means the service id.
*
*
* - localhost:8080
* - http://localhost:8080
* - https://localhost:8080
* - localhost:8080/api
* - user(service id)
*
*/
private String baseUrl;
/**
* Default headers will be added to all the requests.
*/
private List headers = new ArrayList<>();
/**
* Channels configuration.
*/
private List channels = new ArrayList<>();
/**
* Whether to convert Java bean to query parameters, default value is {@code false}.
*/
private boolean beanToQueryEnabled = false;
/**
* Refresh configuration.
*/
private Refresh refresh = new Refresh();
/**
* Client Type, if not specified, an appropriate client type will be set.
*
*
* - Use {@link ClientType#REST_CLIENT} if none of the methods in the client return Reactive type.
*
- Use {@link ClientType#WEB_CLIENT} if any method in the client returns Reactive type.
*
*
* In most cases, there's no need to explicitly specify the client type.
*
*
NOTE: the {@link #connectTimeout} and {@link #readTimeout} settings are not supported by {@link ClientType#WEB_CLIENT}.
*
* @see ClientType
* @since 3.2.0
*/
private ClientType clientType;
/**
* whether to process {@link org.springframework.web.bind.annotation.RequestMapping} based annotation,
* default {@code false}.
*
*
Recommending to use {@link org.springframework.web.service.annotation.HttpExchange} instead of {@link org.springframework.web.bind.annotation.RequestMapping}.
*
* @since 3.2.0
*/
private boolean requestMappingSupportEnabled = false;
/**
* Connect timeout duration, specified in milliseconds.
*
* @since 3.2.0
*/
private Integer connectTimeout;
/**
* Read timeout duration, specified in milliseconds.
*
* @since 3.2.0
*/
private Integer readTimeout;
/**
* Whether to check unused configuration, default {@code true}.
*
* @since 3.2.0
*/
private boolean warnUnusedConfigEnabled = true;
/**
* Whether to enable loadbalancer, default {@code true}.
*
*
Prerequisites:
*
* - {@code spring-cloud-starter-loadbalancer} dependency in the classpath.
* - {@code spring.cloud.loadbalancer.enabled=true}
*
*
* @since 3.2.0
*/
private boolean loadbalancerEnabled = true;
/**
* Whether to enable http client reuse, default {@code true}.
*
* Same {@link Channel} configuration will share the same http client if enabled.
*
* @since 3.2.2
*/
private boolean httpClientReuseEnabled = true;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Header {
/**
* Header key.
*/
private String key;
/**
* Header values.
*/
private List values = new ArrayList<>();
}
@Override
public void afterPropertiesSet() {
merge();
}
/**
* Merge default configuration to channels configuration.
*/
void merge() {
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
for (Channel chan : channels) {
mapper.from(baseUrl).when(e -> isNull(chan.getBaseUrl())).to(chan::setBaseUrl);
mapper.from(clientType).when(e -> isNull(chan.getClientType())).to(chan::setClientType);
mapper.from(connectTimeout)
.when(e -> isNull(chan.getConnectTimeout()))
.to(chan::setConnectTimeout);
mapper.from(readTimeout).when(e -> isNull(chan.getReadTimeout())).to(chan::setReadTimeout);
mapper.from(loadbalancerEnabled)
.when(e -> isNull(chan.getLoadbalancerEnabled()))
.to(chan::setLoadbalancerEnabled);
mapper.from(httpClientReuseEnabled)
.when(e -> isNull(chan.getHttpClientReuseEnabled()))
.to(chan::setHttpClientReuseEnabled);
// defaultHeaders + chan.headers
LinkedHashMap> total = headers.stream()
.collect(toMap(Header::getKey, Header::getValues, (oldV, newV) -> oldV, LinkedHashMap::new));
for (Header header : chan.getHeaders()) {
total.put(header.getKey(), header.getValues());
}
List mergedHeaders = total.entrySet().stream()
.map(e -> new Header(e.getKey(), e.getValue()))
.toList();
chan.setHeaders(mergedHeaders);
}
}
HttpExchangeProperties.Channel defaultClient() {
return new Channel(
null,
baseUrl,
headers,
clientType,
connectTimeout,
readTimeout,
loadbalancerEnabled,
httpClientReuseEnabled,
List.of(),
List.of());
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Channel {
/**
* Optional channel name.
*/
private String name;
/**
* Base url, use {@link HttpExchangeProperties#baseUrl} if not set.
*/
private String baseUrl;
/**
* Default headers will be merged with {@link HttpExchangeProperties#headers}.
*/
private List headers = new ArrayList<>();
/**
* Client type, use {@link HttpExchangeProperties#clientType} if not set.
*
* NOTE:
* the {@link #connectTimeout} and {@link #readTimeout} settings are not supported for {@link ClientType#WEB_CLIENT}.
*
* @see ClientType
*/
private ClientType clientType;
/**
* Connection timeout duration, specified in milliseconds.
*
*
Use {@link HttpExchangeProperties#connectTimeout} if not set.
*
* @see HttpExchangeProperties#connectTimeout
* @since 3.2.0
*/
private Integer connectTimeout;
/**
* Read timeout duration, specified in milliseconds.
*
*
Use {@link HttpExchangeProperties#readTimeout} if not set.
*
* @see HttpExchangeProperties#readTimeout
* @since 3.2.0
*/
private Integer readTimeout;
/**
* Whether to enable loadbalancer, use {@link HttpExchangeProperties#loadbalancerEnabled} if not set.
*
* @see HttpExchangeProperties#loadbalancerEnabled
* @since 3.2.0
*/
private Boolean loadbalancerEnabled;
/**
* Whether to enable http client reuse, use {@link HttpExchangeProperties#httpClientReuseEnabled} if not set.
*
* @see HttpExchangeProperties#httpClientReuseEnabled
* @since 3.2.2
*/
private Boolean httpClientReuseEnabled;
/**
* Exchange Clients to apply this channel.
*
*
e.g. client {@code com.example.client.ExampleClient} can be identified by
*
* - {@code ExampleClient}, {@code exampleClient}, {@code example-client} (Class simple name)
*
- {@code com.example.client.ExampleClient} (Class canonical name)
*
- {@code com.**.*Client}, {@code com.example.**} (Ant style pattern)
*
*
* This is a more flexible alternative to {@link HttpExchangeProperties.Channel#classes}.
*
* @see Class#getCanonicalName()
* @see org.springframework.util.AntPathMatcher
*/
private List clients = new ArrayList<>();
/**
* Exchange Client classes to apply this channel.
*
* This is a more IDE-friendly alternative to {@link HttpExchangeProperties.Channel#clients}.
*/
private List> classes = new ArrayList<>();
}
@Data
public static class Refresh {
public static final String PREFIX = HttpExchangeProperties.PREFIX + ".refresh";
/**
* Whether to enable refresh exchange clients, default {@code false}.
*
* This feature needs {@code spring-cloud-context} dependency in the classpath.
*
*
NOTE: This feature is not supported by native image.
*
* @see Refresh Scope
*/
private boolean enabled = false;
}
public enum ClientType {
/**
* @see RestClient
*/
REST_CLIENT,
/**
* @see WebClient
*/
WEB_CLIENT,
/**
* @see RestTemplate
*/
REST_TEMPLATE
}
}