diff --git a/samples/grpc-server/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java b/samples/grpc-server/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java index a657807..7e4fc5e 100644 --- a/samples/grpc-server/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java +++ b/samples/grpc-server/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java @@ -15,6 +15,7 @@ import org.springframework.grpc.sample.proto.HelloRequest; import org.springframework.grpc.sample.proto.SimpleGrpc; import org.springframework.test.annotation.DirtiesContext; +import io.grpc.Channel; import io.grpc.Grpc; import io.grpc.InsecureChannelCredentials; @@ -45,11 +46,17 @@ public class GrpcServerApplicationTests { @TestConfiguration static class ExtraConfiguration { + @Bean - SimpleGrpc.SimpleBlockingStub stub() { - var channel = Grpc.newChannelBuilderForAddress("0.0.0.0", 9090, InsecureChannelCredentials.create()) - .build(); + SimpleGrpc.SimpleBlockingStub stub(Channel channel) { return SimpleGrpc.newBlockingStub(channel); } + + @Bean(destroyMethod = "shutdown") + Channel channel() { + return Grpc.newChannelBuilderForAddress("0.0.0.0", 9090, InsecureChannelCredentials.create()).build(); + } + } + } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelFactory.java new file mode 100644 index 0000000..2265880 --- /dev/null +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024-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 io.grpc.Channel; + +public interface GrpcChannelFactory extends AutoCloseable { + + Channel createChannel(String name); + +} diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/AbstractGrpcServerFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java similarity index 87% rename from spring-grpc-core/src/main/java/org/springframework/grpc/server/AbstractGrpcServerFactory.java rename to spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java index 9a71517..884606a 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/AbstractGrpcServerFactory.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 the original author or authors. + * Copyright 2024-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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Partial copy from net.devh:grpc-spring-boot-starter. */ package org.springframework.grpc.server; @@ -32,7 +31,7 @@ import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.ServerServiceDefinition; -public abstract class AbstractGrpcServerFactory> implements GrpcServerFactory { +public class DefaultGrpcServerFactory> implements GrpcServerFactory { /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @@ -45,7 +44,7 @@ public abstract class AbstractGrpcServerFactory> impl private final int port; - protected AbstractGrpcServerFactory(String address, int port, final List serverConfigurers) { + public DefaultGrpcServerFactory(String address, int port, final List serverConfigurers) { this.serverConfigurers = requireNonNull(serverConfigurers, "serverConfigurers"); this.address = address; this.port = port; @@ -70,7 +69,11 @@ public abstract class AbstractGrpcServerFactory> impl * Creates a new server builder. * @return The newly created server builder. */ - protected abstract T newServerBuilder(); + @SuppressWarnings("unchecked") + protected T newServerBuilder() { + // TODO: Add support for address resolution + return (T) ServerBuilder.forPort(getPort()); + } /** * Configures the given server builder. This method can be overwritten to add features diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java deleted file mode 100644 index b606e86..0000000 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2016-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 - * - * http://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. - * - * Partial copy from net.devh:grpc-spring-boot-starter. - */ - -package org.springframework.grpc.server; - -import java.net.InetSocketAddress; -import java.util.List; - -import org.springframework.grpc.util.GrpcUtils; - -import com.google.common.net.InetAddresses; - -import io.grpc.netty.NettyServerBuilder; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.epoll.EpollServerDomainSocketChannel; -import io.netty.channel.unix.DomainSocketAddress; - -/** - * Factory for netty based grpc servers. - * - * @author Michael (yidongnan@gmail.com) - * @author Dave Syer - */ -public class NettyGrpcServerFactory extends AbstractGrpcServerFactory { - - /** - * Creates a new netty server factory with the given properties. - * @param address The address to bind the server to. - * @param port The port to bind the server to (0 for ephemeral port). - * @param properties The properties used to configure the server. - * @param serverConfigurers The server configurers to use. Can be empty. - */ - public NettyGrpcServerFactory(String address, int port, final List serverConfigurers) { - super(address, port, serverConfigurers); - } - - @Override - protected NettyServerBuilder newServerBuilder() { - if (getAddres().startsWith(GrpcUtils.DOMAIN_SOCKET_ADDRESS_PREFIX)) { - final String path = GrpcUtils.extractDomainSocketAddressPath(getAddres()); - return NettyServerBuilder.forAddress(new DomainSocketAddress(path)) - .channelType(EpollServerDomainSocketChannel.class) - .bossEventLoopGroup(new EpollEventLoopGroup(1)) - .workerEventLoopGroup(new EpollEventLoopGroup()); - } - else if (GrpcUtils.ANY_IP_ADDRESS.equals(getAddres())) { - return NettyServerBuilder.forPort(getPort()); - } - else { - return NettyServerBuilder - .forAddress(new InetSocketAddress(InetAddresses.forString(getAddres()), getPort())); - } - } - -} diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/util/GrpcUtils.java b/spring-grpc-core/src/main/java/org/springframework/grpc/util/GrpcUtils.java deleted file mode 100644 index 8b757fa..0000000 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/util/GrpcUtils.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016-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 - * - * http://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. - * - * Partial copy from net.devh:grpc-spring-boot-starter. - */ -package org.springframework.grpc.util; - -import io.grpc.MethodDescriptor; - -/** - * Utility class that contains methods to extract some information from grpc classes. - * - * @author Daniel Theuke (daniel.theuke@heuboe.de) - * @author Dave Syer - */ -public final class GrpcUtils { - - /** - * A constant that defines the scheme of a Unix domain socket address. - */ - public static final String DOMAIN_SOCKET_ADDRESS_SCHEME = "unix"; - - /** - * A constant that defines the scheme prefix of a Unix domain socket address. - */ - public static final String DOMAIN_SOCKET_ADDRESS_PREFIX = DOMAIN_SOCKET_ADDRESS_SCHEME + ":"; - - /** - * A constant that defines that the server should listen to any IPv4 and IPv6 address. - */ - public static final String ANY_IP_ADDRESS = "*"; - - /** - * Extracts the domain socket address specific path from the given full address. The - * address must fulfill the requirements as specified by - * grpc. - * @param address The address to extract it from. - * @return The extracted domain socket address specific path. - * @throws IllegalArgumentException If the given address is not a valid address. - */ - public static String extractDomainSocketAddressPath(final String address) { - if (!address.startsWith(DOMAIN_SOCKET_ADDRESS_PREFIX)) { - throw new IllegalArgumentException(address + " is not a valid domain socket address."); - } - String path = address.substring(DOMAIN_SOCKET_ADDRESS_PREFIX.length()); - if (path.startsWith("//")) { - path = path.substring(2); - // We don't check this as there is no reliable way to check that it's an - // absolute path, especially when Windows adds support for these in the future - // if (!path.startsWith("/")) { - // throw new IllegalArgumentException("If the path is prefixed with '//', then - // the path must be absolute"); - // } - } - return path; - } - - /** - * Extracts the service name from the given method. - * @param method The method to get the service name from. - * @return The extracted service name. - * @see MethodDescriptor#extractFullServiceName(String) - * @see #extractMethodName(MethodDescriptor) - */ - public static String extractServiceName(final MethodDescriptor method) { - return MethodDescriptor.extractFullServiceName(method.getFullMethodName()); - } - - /** - * Extracts the method name from the given method. - * @param method The method to get the method name from. - * @return The extracted method name. - * @see #extractServiceName(MethodDescriptor) - */ - public static String extractMethodName(final MethodDescriptor method) { - // This method is the equivalent of MethodDescriptor.extractFullServiceName - final String fullMethodName = method.getFullMethodName(); - final int index = fullMethodName.lastIndexOf('/'); - if (index == -1) { - return fullMethodName; - } - return fullMethodName.substring(index + 1); - } - - private GrpcUtils() { - } - -} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java new file mode 100644 index 0000000..f804764 --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024-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.autoconfigure.client; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Configuration; +import org.springframework.grpc.server.GrpcServerFactory; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(GrpcServerFactory.class) +public class GrpcClientAutoConfiguration { + +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java index 01ca96b..8828c0c 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java @@ -17,17 +17,14 @@ package org.springframework.grpc.autoconfigure.server; import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.grpc.server.DefaultGrpcServerFactory; import org.springframework.grpc.server.GrpcServerConfigurer; import org.springframework.grpc.server.GrpcServerFactory; -import org.springframework.grpc.server.NettyGrpcServerFactory; import io.grpc.ServerServiceDefinition; @@ -37,17 +34,12 @@ import io.grpc.ServerServiceDefinition; @EnableConfigurationProperties(GrpcServerProperties.class) public class GrpcServerFactoryAutoConfiguration { - private static final Log logger = LogFactory.getLog(GrpcServerFactoryAutoConfiguration.class); - @ConditionalOnMissingBean(GrpcServerFactory.class) - @ConditionalOnClass(name = { "io.netty.channel.Channel", "io.grpc.netty.NettyServerBuilder" }) @Bean - public NettyGrpcServerFactory nettyGrpcServerFactory(final GrpcServerProperties properties, + public DefaultGrpcServerFactory defaultGrpcServerFactory(final GrpcServerProperties properties, final GrpcServiceDiscoverer serviceDiscoverer, final List serverConfigurers) { - - logger.info("Detected grpc-netty: Creating NettyGrpcServerFactory"); - final NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), properties.getPort(), - serverConfigurers); + final DefaultGrpcServerFactory factory = new DefaultGrpcServerFactory<>(properties.getAddress(), + properties.getPort(), serverConfigurers); for (final ServerServiceDefinition service : serviceDiscoverer.findGrpcServices()) { factory.addService(service); } diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerProperties.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerProperties.java index 7e1b23d..388f499 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerProperties.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerProperties.java @@ -20,12 +20,11 @@ import java.time.temporal.ChronoUnit; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; -import org.springframework.grpc.util.GrpcUtils; @ConfigurationProperties(prefix = "spring.grpc.server") public class GrpcServerProperties { - private String address = GrpcUtils.ANY_IP_ADDRESS; + private String address = "*"; private int port = 9090; diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServiceAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServiceAutoConfiguration.java index aa938da..79ca3e5 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServiceAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServiceAutoConfiguration.java @@ -37,7 +37,7 @@ public class GrpcServiceAutoConfiguration { @ConditionalOnMissingBean @Bean public GrpcServiceDiscoverer defaultGrpcServiceDiscoverer(ApplicationContext applicationContext) { - return new AnnotationGrpcServiceDiscoverer(applicationContext); + return new DefaultGrpcServiceDiscoverer(applicationContext); } @ConditionalOnMissingBean @@ -49,11 +49,11 @@ public class GrpcServiceAutoConfiguration { } -class AnnotationGrpcServiceDiscoverer implements GrpcServiceDiscoverer { +class DefaultGrpcServiceDiscoverer implements GrpcServiceDiscoverer { private final ApplicationContext applicationContext; - public AnnotationGrpcServiceDiscoverer(ApplicationContext applicationContext) { + public DefaultGrpcServiceDiscoverer(ApplicationContext applicationContext) { this.applicationContext = applicationContext; }