Skip to content

Improve GrpcChannelFactory API #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 3, 2025
Merged
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 @@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -51,7 +52,7 @@ static class TestListener {
@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalGrpcPort int port) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build());
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -50,7 +51,7 @@ static class ExtraConfiguration {
@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalGrpcPort int port) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build());
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class WithClientHealthEnabled {
@Test
void loadBalancerRespectsServerHealth(@Autowired GrpcChannelFactory channels,
@Autowired HealthStatusManager healthStatusManager) {
ManagedChannel channel = channels.createChannel("health-test").build();
ManagedChannel channel = channels.createChannel("health-test");
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channel);

// put the service up (SERVING) and give load balancer time to update
Expand Down Expand Up @@ -117,7 +117,7 @@ class WithActuatorHealthAdapter {

@Test
void healthIndicatorsAdaptedToGrpcHealthStatus(@Autowired GrpcChannelFactory channels) {
var channel = channels.createChannel("0.0.0.0:0").build();
var channel = channels.createChannel("0.0.0.0:0");
var healthStub = HealthGrpc.newBlockingStub(channel);
var serviceName = "custom";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.grpc.autoconfigure.server.GrpcServerProperties;
import org.springframework.grpc.client.ChannelBuilderOptions;
import org.springframework.grpc.client.GrpcChannelFactory;
import org.springframework.grpc.sample.proto.HelloReply;
import org.springframework.grpc.sample.proto.HelloRequest;
Expand All @@ -36,8 +38,9 @@
import org.springframework.test.context.ActiveProfiles;

import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.grpc.Status.Code;
import io.grpc.StatusRuntimeException;
import io.grpc.netty.NettyChannelBuilder;

/**
* More detailed integration tests for {@link GrpcServerFactory gRPC server factories} and
Expand All @@ -51,7 +54,7 @@ class ServerWithInProcessChannel {

@Test
void servesResponseToClient(@Autowired GrpcChannelFactory channels) {
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:0").build());
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:0"));
}

}
Expand All @@ -62,8 +65,7 @@ class ServerWithException {

@Test
void specificErrorResponse(@Autowired GrpcChannelFactory channels) {
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc
.newBlockingStub(channels.createChannel("0.0.0.0:0").build());
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:0"));
assertThat(assertThrows(StatusRuntimeException.class,
() -> client.sayHello(HelloRequest.newBuilder().setName("error").build()))
.getStatus()
Expand All @@ -72,8 +74,7 @@ void specificErrorResponse(@Autowired GrpcChannelFactory channels) {

@Test
void defaultErrorResponseIsUnknown(@Autowired GrpcChannelFactory channels) {
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc
.newBlockingStub(channels.createChannel("0.0.0.0:0").build());
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:0"));
assertThat(assertThrows(StatusRuntimeException.class,
() -> client.sayHello(HelloRequest.newBuilder().setName("internal").build()))
.getStatus()
Expand All @@ -88,8 +89,7 @@ class ServerWithUnhandledException {

@Test
void specificErrorResponse(@Autowired GrpcChannelFactory channels) {
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc
.newBlockingStub(channels.createChannel("0.0.0.0:0").build());
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:0"));
assertThat(assertThrows(StatusRuntimeException.class,
() -> client.sayHello(HelloRequest.newBuilder().setName("error").build()))
.getStatus()
Expand All @@ -98,8 +98,7 @@ void specificErrorResponse(@Autowired GrpcChannelFactory channels) {

@Test
void defaultErrorResponseIsUnknown(@Autowired GrpcChannelFactory channels) {
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc
.newBlockingStub(channels.createChannel("0.0.0.0:0").build());
SimpleGrpc.SimpleBlockingStub client = SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:0"));
assertThat(assertThrows(StatusRuntimeException.class,
() -> client.sayHello(HelloRequest.newBuilder().setName("internal").build()))
.getStatus()
Expand All @@ -116,7 +115,7 @@ class ServerWithAnyIPv4AddressAndRandomPort {
@Test
void servesResponseToClientWithAnyIPv4AddressAndRandomPort(@Autowired GrpcChannelFactory channels,
@LocalGrpcPort int port) {
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:" + port).build());
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:" + port));
}

}
Expand All @@ -129,7 +128,7 @@ class ServerWithAnyIPv6AddressAndRandomPort {
@Test
void servesResponseToClientWithAnyIPv4AddressAndRandomPort(@Autowired GrpcChannelFactory channels,
@LocalGrpcPort int port) {
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:" + port).build());
assertThatResponseIsServedToChannel(channels.createChannel("0.0.0.0:" + port));
}

}
Expand All @@ -142,7 +141,7 @@ class ServerWithLocalhostAndRandomPort {
@Test
void servesResponseToClientWithLocalhostAndRandomPort(@Autowired GrpcChannelFactory channels,
@LocalGrpcPort int port) {
assertThatResponseIsServedToChannel(channels.createChannel("127.0.0.1:" + port).build());
assertThatResponseIsServedToChannel(channels.createChannel("127.0.0.1:" + port));
}

}
Expand All @@ -156,7 +155,7 @@ class ServerConfiguredWithStaticClientChannel {

@Test
void servesResponseToClientWithConfiguredChannel(@Autowired GrpcChannelFactory channels) {
assertThatResponseIsServedToChannel(channels.createChannel("test-channel").build());
assertThatResponseIsServedToChannel(channels.createChannel("test-channel"));
}

}
Expand All @@ -169,7 +168,8 @@ class ServerWithUnixDomain {

@Test
void clientChannelWithUnixDomain(@Autowired GrpcChannelFactory channels) {
assertThatResponseIsServedToChannel(channels.createChannel("unix:unix-test-channel").build());
assertThatResponseIsServedToChannel(channels.createChannel("unix:unix-test-channel",
ChannelBuilderOptions.defaults().<NettyChannelBuilder>withCustomizer((__, b) -> b.usePlaintext())));
}

}
Expand All @@ -185,7 +185,7 @@ class ServerWithSsl {

@Test
void clientChannelWithSsl(@Autowired GrpcChannelFactory channels) {
assertThatResponseIsServedToChannel(channels.createChannel("test-channel").build());
assertThatResponseIsServedToChannel(channels.createChannel("test-channel"));
}

}
Expand All @@ -203,7 +203,7 @@ class ServerWithClientAuth {

@Test
void clientChannelWithSsl(@Autowired GrpcChannelFactory channels) {
assertThatResponseIsServedToChannel(channels.createChannel("test-channel").build());
assertThatResponseIsServedToChannel(channels.createChannel("test-channel"));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -49,7 +50,7 @@ static class ExtraConfiguration {
@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalServerPort int port) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build());
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -51,7 +52,7 @@ static class ExtraConfiguration {
@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalGrpcPort int port) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build());
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -51,7 +52,7 @@ static class ExtraConfiguration {
@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalGrpcPort int port) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build());
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2023-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.grpc.client;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

import org.springframework.util.Assert;

import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannelBuilder;

/**
* Options used by {@link GrpcChannelFactory} when building channels.
* <p>
* Provides functionality beyond what is available with the native channel builders (e.g.
* {@code shutdownGracePeriod}) and overrides some native channel builder behavior (e.g.
* {@code interceptors}.
*
* @author Chris Bono
*/
public final class ChannelBuilderOptions {

private final List<ClientInterceptor> interceptors;

private final boolean mergeWithGlobalInterceptors;

private final Duration shutdownGracePeriod;

@SuppressWarnings("rawtypes")
private final GrpcChannelBuilderCustomizer customizer;

@SuppressWarnings("rawtypes")
private ChannelBuilderOptions(List<ClientInterceptor> interceptors, boolean mergeWithGlobalInterceptors,
Duration shutdownGracePeriod, GrpcChannelBuilderCustomizer customizer) {
this.interceptors = Collections.unmodifiableList(interceptors);
this.mergeWithGlobalInterceptors = mergeWithGlobalInterceptors;
this.shutdownGracePeriod = shutdownGracePeriod;
this.customizer = customizer;
}

/**
* Gets the client interceptors to apply to the channel.
* @return the client interceptors to apply to the channel
*/
public List<ClientInterceptor> interceptors() {
return this.interceptors;
}

/**
* Gets whether the provided interceptors should be blended with the global
* interceptors.
* @return whether the provided interceptors should be blended with the global
* interceptors (default false)
*/
public boolean mergeWithGlobalInterceptors() {
return this.mergeWithGlobalInterceptors;
}

/**
* Gets the time to wait for the channel to gracefully shutdown.
* @return the time to wait for the channel to gracefully shutdown (default of 30s)
*/
public Duration shutdownGracePeriod() {
return this.shutdownGracePeriod;
}

/**
* Gets the customizer to apply to the builder used to create the channel.
* @param <T> the type of the builder the customizer operates on
* @return the customizer to apply (default of
* {@link GrpcChannelBuilderCustomizer#defaults()})
*/
@SuppressWarnings("unchecked")
public <T extends ManagedChannelBuilder<T>> GrpcChannelBuilderCustomizer<T> customizer() {
return (GrpcChannelBuilderCustomizer<T>) this.customizer;
}

/**
* Gets a new immutable options instance populated with default values.
* @return a new immutable options instance populated with default values.
*/
public static ChannelBuilderOptions defaults() {
return new ChannelBuilderOptions(List.of(), false, Duration.ofSeconds(30),
GrpcChannelBuilderCustomizer.defaults());
}

/**
* Set the client interceptors to apply to the channel.
* @param interceptors list of client interceptors to apply to the channel or empty
* list to clear out any previously set interceptors
* @return a new immutable options instance populated with the specified interceptors
* and the settings of this current options instance.
*/
public ChannelBuilderOptions withInterceptors(List<ClientInterceptor> interceptors) {
Assert.notNull(interceptors, "interceptors must not be null");
return new ChannelBuilderOptions(interceptors, this.mergeWithGlobalInterceptors, this.shutdownGracePeriod,
this.customizer);
}

/**
* Set whether the provided interceptors should be blended with the global
* interceptors.
* @param mergeWithGlobalInterceptors whether the provided interceptors should be
* @return a new immutable options instance populated with the specified merge setting
* and the settings of this current options instance.
*/
public ChannelBuilderOptions withInterceptorsMerge(boolean mergeWithGlobalInterceptors) {
return new ChannelBuilderOptions(this.interceptors, mergeWithGlobalInterceptors, this.shutdownGracePeriod,
this.customizer);
}

/**
* Set the time to wait for the channel to gracefully shutdown.
* @param shutdownGracePeriod the time to wait for the channel to gracefully shutdown
* @return a new immutable options instance populated with the specified
* {@code shutdownGracePeriod} and the settings of this current options instance.
*/
public ChannelBuilderOptions withShutdownGracePeriod(Duration shutdownGracePeriod) {
return new ChannelBuilderOptions(this.interceptors, this.mergeWithGlobalInterceptors, shutdownGracePeriod,
this.customizer);
}

/**
* Set the customizer to apply to the builder used to create the channel.
* @param <T> type of builder the customizer operates on
* @param customizer the customizer to apply to the builder used to create the channel
* @return a new immutable options instance populated with the specified
* {@code customizer} and the settings of this current options instance.
*/
public <T extends ManagedChannelBuilder<T>> ChannelBuilderOptions withCustomizer(
GrpcChannelBuilderCustomizer<T> customizer) {
return new ChannelBuilderOptions(this.interceptors, this.mergeWithGlobalInterceptors, this.shutdownGracePeriod,
customizer);
}

}
Loading
Loading