Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

import org.jspecify.annotations.Nullable;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
Expand Down Expand Up @@ -52,6 +54,11 @@ public class GrpcClientProperties implements EnvironmentAware, VirtualTargets {
*/
private final Map<String, ChannelConfig> channels = new HashMap<>();

/**
* Default configuration that named channels can inherit from.
*/
private final Channel channel = new Channel();

/**
* The default channel configuration to use for new channels.
*/
Expand All @@ -73,6 +80,10 @@ public Map<String, ChannelConfig> getChannels() {
return this.channels;
}

public Channel getChannel() {
return this.channel;
}

public ChannelConfig getDefaultChannel() {
return this.defaultChannel;
}
Expand Down Expand Up @@ -101,11 +112,14 @@ public ChannelConfig getChannel(String name) {
if ("default".equals(name)) {
return this.defaultChannel;
}
ChannelConfig channel = this.channels.get(name);
if (channel != null) {
return channel;
ChannelConfig namedChannel = this.channels.get(name);
if (namedChannel != null) {
if (namedChannel.isInheritDefaults()) {
return this.channel.getDefaults().mergeWith(namedChannel);
}
return namedChannel;
}
channel = this.defaultChannel.copy();
ChannelConfig newChannel = this.defaultChannel.copy();
String address = name;
if (!name.contains(":/") && !name.startsWith("unix:")) {
if (name.contains(":")) {
Expand All @@ -118,8 +132,8 @@ public ChannelConfig getChannel(String name) {
}
}
}
channel.setAddress(address);
return channel;
newChannel.setAddress(address);
return newChannel;
}

@Override
Expand All @@ -140,7 +154,7 @@ public static class ChannelConfig {
/**
* The target address uri to connect to.
*/
private String address = "static://localhost:9090";
private @Nullable String address;

/**
* The default deadline for RPCs performed on this channel.
Expand All @@ -150,63 +164,63 @@ public static class ChannelConfig {
/**
* The load balancing policy the channel should use.
*/
private String defaultLoadBalancingPolicy = "round_robin";
private @Nullable String defaultLoadBalancingPolicy;

/**
* Whether keep alive is enabled on the channel.
*/
private boolean enableKeepAlive;
private @Nullable Boolean enableKeepAlive;

private final Health health = new Health();

/**
* The duration without ongoing RPCs before going to idle mode.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration idleTimeout = Duration.ofSeconds(20);
private @Nullable Duration idleTimeout;

/**
* The delay before sending a keepAlive. Note that shorter intervals increase the
* network burden for the server and this value can not be lower than
* 'permitKeepAliveTime' on the server.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration keepAliveTime = Duration.ofMinutes(5);
private @Nullable Duration keepAliveTime;

/**
* The default timeout for a keepAlives ping request.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration keepAliveTimeout = Duration.ofSeconds(20);
private @Nullable Duration keepAliveTimeout;

/**
* Whether a keepAlive will be performed when there are no outstanding RPC on a
* connection.
*/
private boolean keepAliveWithoutCalls;
private @Nullable Boolean keepAliveWithoutCalls;

/**
* Maximum message size allowed to be received by the channel (default 4MiB). Set
* to '-1' to use the highest possible limit (not recommended).
*/
private DataSize maxInboundMessageSize = DataSize.ofBytes(4194304);
private @Nullable DataSize maxInboundMessageSize;

/**
* Maximum metadata size allowed to be received by the channel (default 8KiB). Set
* to '-1' to use the highest possible limit (not recommended).
*/
private DataSize maxInboundMetadataSize = DataSize.ofBytes(8192);
private @Nullable DataSize maxInboundMetadataSize;

/**
* The negotiation type for the channel.
*/
private NegotiationType negotiationType = NegotiationType.PLAINTEXT;
private @Nullable NegotiationType negotiationType;

/**
* Flag to say that strict SSL checks are not enabled (so the remote certificate
* could be anonymous).
*/
private boolean secure = true;
private @Nullable Boolean secure;

/**
* Map representation of the service config to use for the channel.
Expand All @@ -220,8 +234,13 @@ public static class ChannelConfig {
*/
private @Nullable String userAgent;

/**
* Whether to inherit settings from the channel defaults configuration.
*/
private boolean inheritDefaults;

public String getAddress() {
return this.address;
return Objects.requireNonNullElse(this.address, "static://localhost:9090");
}

public void setAddress(final String address) {
Expand All @@ -237,15 +256,15 @@ public void setDefaultDeadline(@Nullable Duration defaultDeadline) {
}

public String getDefaultLoadBalancingPolicy() {
return this.defaultLoadBalancingPolicy;
return Objects.requireNonNullElse(this.defaultLoadBalancingPolicy, "round_robin");
}

public void setDefaultLoadBalancingPolicy(final String defaultLoadBalancingPolicy) {
this.defaultLoadBalancingPolicy = defaultLoadBalancingPolicy;
}

public boolean isEnableKeepAlive() {
return this.enableKeepAlive;
return Objects.requireNonNullElse(this.enableKeepAlive, false);
}

public void setEnableKeepAlive(boolean enableKeepAlive) {
Expand All @@ -257,39 +276,39 @@ public Health getHealth() {
}

public Duration getIdleTimeout() {
return this.idleTimeout;
return Objects.requireNonNullElse(this.idleTimeout, Duration.ofSeconds(20));
}

public void setIdleTimeout(Duration idleTimeout) {
this.idleTimeout = idleTimeout;
}

public Duration getKeepAliveTime() {
return this.keepAliveTime;
return Objects.requireNonNullElse(this.keepAliveTime, Duration.ofMinutes(5));
}

public void setKeepAliveTime(Duration keepAliveTime) {
this.keepAliveTime = keepAliveTime;
}

public Duration getKeepAliveTimeout() {
return this.keepAliveTimeout;
return Objects.requireNonNullElse(this.keepAliveTimeout, Duration.ofSeconds(20));
}

public void setKeepAliveTimeout(Duration keepAliveTimeout) {
this.keepAliveTimeout = keepAliveTimeout;
}

public boolean isKeepAliveWithoutCalls() {
return this.keepAliveWithoutCalls;
return Objects.requireNonNullElse(this.keepAliveWithoutCalls, false);
}

public void setKeepAliveWithoutCalls(boolean keepAliveWithoutCalls) {
this.keepAliveWithoutCalls = keepAliveWithoutCalls;
}

public DataSize getMaxInboundMessageSize() {
return this.maxInboundMessageSize;
return Objects.requireNonNullElse(this.maxInboundMessageSize, DataSize.ofBytes(4194304));
}

public void setMaxInboundMessageSize(final DataSize maxInboundMessageSize) {
Expand All @@ -298,7 +317,7 @@ public void setMaxInboundMessageSize(final DataSize maxInboundMessageSize) {
}

public DataSize getMaxInboundMetadataSize() {
return this.maxInboundMetadataSize;
return Objects.requireNonNullElse(this.maxInboundMetadataSize, DataSize.ofBytes(8192));
}

public void setMaxInboundMetadataSize(DataSize maxInboundMetadataSize) {
Expand All @@ -319,15 +338,15 @@ else if (maxSize != null && maxSize.toBytes() == -1) {
}

public NegotiationType getNegotiationType() {
return this.negotiationType;
return Objects.requireNonNullElse(this.negotiationType, NegotiationType.PLAINTEXT);
}

public void setNegotiationType(NegotiationType negotiationType) {
this.negotiationType = negotiationType;
}

public boolean isSecure() {
return this.secure;
return Objects.requireNonNullElse(this.secure, true);
}

public void setSecure(boolean secure) {
Expand All @@ -350,6 +369,14 @@ public void setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
}

public boolean isInheritDefaults() {
return this.inheritDefaults;
}

public void setInheritDefaults(boolean inheritDefaults) {
this.inheritDefaults = inheritDefaults;
}

/**
* Provide a copy of the channel instance.
* @return a copy of the channel instance.
Expand All @@ -368,13 +395,38 @@ ChannelConfig copy() {
copy.maxInboundMetadataSize = this.maxInboundMetadataSize;
copy.userAgent = this.userAgent;
copy.defaultDeadline = this.defaultDeadline;
copy.inheritDefaults = this.inheritDefaults;
copy.health.copyValuesFrom(this.getHealth());
copy.secure = this.secure;
copy.ssl.copyValuesFrom(this.getSsl());
copy.serviceConfig.putAll(this.serviceConfig);
return copy;
}

ChannelConfig mergeWith(ChannelConfig other) {
ChannelConfig merged = this.copy();
PropertyMapper map = PropertyMapper.get();
map.from(other.address).to(merged::setAddress);
map.from(other.defaultDeadline).to(merged::setDefaultDeadline);
map.from(other.defaultLoadBalancingPolicy).to(merged::setDefaultLoadBalancingPolicy);
map.from(other.enableKeepAlive).to(merged::setEnableKeepAlive);
map.from(other.idleTimeout).to(merged::setIdleTimeout);
map.from(other.keepAliveTime).to(merged::setKeepAliveTime);
map.from(other.keepAliveTimeout).to(merged::setKeepAliveTimeout);
map.from(other.keepAliveWithoutCalls).to(merged::setKeepAliveWithoutCalls);
map.from(other.maxInboundMessageSize).to(merged::setMaxInboundMessageSize);
map.from(other.maxInboundMetadataSize).to(merged::setMaxInboundMetadataSize);
map.from(other.negotiationType).to(merged::setNegotiationType);
map.from(other.secure).to(merged::setSecure);
map.from(other.userAgent).to(merged::setUserAgent);
merged.health.mergeWith(other.health);
merged.ssl.mergeWith(other.ssl);
if (!other.serviceConfig.isEmpty()) {
merged.serviceConfig.putAll(other.serviceConfig);
}
return merged;
}

/**
* Extracts the service configuration from the client properties, respecting the
* yaml lists (e.g. `retryPolicy`).
Expand All @@ -390,15 +442,15 @@ public static class Health {
/**
* Whether to enable client-side health check for the channel.
*/
private boolean enabled;
private @Nullable Boolean enabled;

/**
* Name of the service to check health on.
*/
private @Nullable String serviceName;

public boolean isEnabled() {
return this.enabled;
return Objects.requireNonNullElse(this.enabled, false);
}

public void setEnabled(boolean enabled) {
Expand All @@ -422,6 +474,12 @@ void copyValuesFrom(Health other) {
this.serviceName = other.serviceName;
}

void mergeWith(Health other) {
PropertyMapper map = PropertyMapper.get();
map.from(other.enabled).to(this::setEnabled);
map.from(other.serviceName).to(this::setServiceName);
}

}

public static class Ssl {
Expand Down Expand Up @@ -466,6 +524,25 @@ void copyValuesFrom(Ssl other) {
this.bundle = other.bundle;
}

void mergeWith(Ssl other) {
PropertyMapper map = PropertyMapper.get();
map.from(other.enabled).to(this::setEnabled);
map.from(other.bundle).to(this::setBundle);
}

}

}

/**
* Container for channel defaults configuration.
*/
public static class Channel {

private final ChannelConfig defaults = new ChannelConfig();

public ChannelConfig getDefaults() {
return this.defaults;
}

}
Expand Down
Loading
Loading