diff --git a/samples/grpc-tomcat/README.md b/samples/grpc-tomcat/README.md new file mode 100644 index 0000000..975f063 --- /dev/null +++ b/samples/grpc-tomcat/README.md @@ -0,0 +1,61 @@ +# Spring Boot gRPC Sample + +This project is a copy one of the samples from the [gRPC Spring Boot Starter](https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/examples/local-grpc-server/build.gradle). Build and run any way you like to run Spring Boot. E.g: + +``` +$ ./mvnw spring-boot:run +... + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v3.0.0) + +2022-12-08T05:32:24.934-08:00 INFO 551632 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 17.0.5 with PID 551632 (/home/dsyer/dev/scratch/demo/target/classes started by dsyer in /home/dsyer/dev/scratch/demo) +2022-12-08T05:32:24.938-08:00 INFO 551632 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default" +2022-12-08T05:32:25.377-08:00 WARN 551632 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: net.devh.boot.grpc.server.autoconfigure.GrpcHealthServiceAutoConfiguration +2022-12-08T05:32:25.416-08:00 WARN 551632 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration +2022-12-08T05:32:25.425-08:00 WARN 551632 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration +2022-12-08T05:32:25.427-08:00 INFO 551632 --- [ main] g.s.a.GrpcServerFactoryAutoConfiguration : Detected grpc-netty: Creating NettyGrpcServerFactory +2022-12-08T05:32:25.712-08:00 INFO 551632 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: Simple, bean: grpcServerService, class: com.example.demo.GrpcServerService +2022-12-08T05:32:25.712-08:00 INFO 551632 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: grpc.health.v1.Health, bean: grpcHealthService, class: io.grpc.protobuf.services.HealthServiceImpl +2022-12-08T05:32:25.712-08:00 INFO 551632 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: grpc.reflection.v1alpha.ServerReflection, bean: protoReflectionService, class: io.grpc.protobuf.services.ProtoReflectionService +2022-12-08T05:32:25.820-08:00 INFO 551632 --- [ main] n.d.b.g.s.s.GrpcServerLifecycle : gRPC Server started, listening on address: *, port: 9090 +2022-12-08T05:32:25.831-08:00 INFO 551632 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.264 seconds (process running for 1.623) +``` + +The server starts by default on port 9090. Test with [gRPCurl](https://github.com/fullstorydev/grpcurl): + +``` +$ grpcurl -d '{"name":"Hi"}' -plaintext localhost:9090 Simple.SayHello +{ + "message": "Hello ==\u003e Hi" +} +``` + +## Native Image + +The app compiles to a native image if the JVM is GraalVM: + +``` +$ ./mvnw -Pnative native:compile +$ ./target/demo + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v3.0.0) + +2022-12-08T05:36:54.365-08:00 INFO 554359 --- [ main] com.example.demo.DemoApplication : Starting AOT-processed DemoApplication using Java 17.0.5 with PID 554359 (/home/dsyer/dev/scratch/demo/target/demo started by dsyer in /home/dsyer/dev/scratch/demo) +2022-12-08T05:36:54.366-08:00 INFO 554359 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default" +2022-12-08T05:36:54.377-08:00 INFO 554359 --- [ main] g.s.a.GrpcServerFactoryAutoConfiguration : Detected grpc-netty: Creating NettyGrpcServerFactory +2022-12-08T05:36:54.392-08:00 INFO 554359 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: Simple, bean: grpcServerService, class: com.example.demo.GrpcServerService +2022-12-08T05:36:54.392-08:00 INFO 554359 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: grpc.health.v1.Health, bean: grpcHealthService, class: io.grpc.protobuf.services.HealthServiceImpl +2022-12-08T05:36:54.392-08:00 INFO 554359 --- [ main] n.d.b.g.s.s.AbstractGrpcServerFactory : Registered gRPC service: grpc.reflection.v1alpha.ServerReflection, bean: protoReflectionService, class: io.grpc.protobuf.services.ProtoReflectionService +2022-12-08T05:36:54.396-08:00 INFO 554359 --- [ main] n.d.b.g.s.s.GrpcServerLifecycle : gRPC Server started, listening on address: *, port: 9090 +2022-12-08T05:36:54.396-08:00 INFO 554359 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.046 seconds (process running for 0.052) +``` diff --git a/samples/grpc-tomcat/pom.xml b/samples/grpc-tomcat/pom.xml new file mode 100644 index 0000000..f98a4f9 --- /dev/null +++ b/samples/grpc-tomcat/pom.xml @@ -0,0 +1,185 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.3 + + + org.springframework.grpc + grpc-tomcat-sample + 0.1.0-SNAPSHOT + Spring gRPC Server Sample + Demo project for Spring gRPC + + + + + + + + + + + + + + + 17 + 0.0.39 + 3.25.5 + 1.63.2 + + + + + org.springframework.grpc + spring-grpc-dependencies + 0.1.0-SNAPSHOT + pom + import + + + + + + org.springframework.grpc + spring-grpc-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + io.grpc + grpc-services + + + io.grpc + grpc-servlet-jakarta + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.grpc + spring-grpc-test + test + + + + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + + + + org.graalvm.buildtools + native-maven-plugin + + + --verbose + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + io.spring.javaformat + spring-javaformat-maven-plugin + ${spring-javaformat-maven-plugin.version} + + + + validate + true + + validate + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + + compile + compile-custom + + + + + + jakarta_omit + + + com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} + grpc-java + + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + \ No newline at end of file diff --git a/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerApplication.java b/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerApplication.java new file mode 100644 index 0000000..090ddfc --- /dev/null +++ b/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerApplication.java @@ -0,0 +1,38 @@ +package org.springframework.grpc.sample; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.grpc.autoconfigure.server.GrpcServerAutoConfiguration; + +import io.grpc.BindableService; +import io.grpc.protobuf.services.ProtoReflectionService; +import io.grpc.servlet.jakarta.GrpcServlet; + +@SpringBootApplication(exclude = GrpcServerAutoConfiguration.class) +public class GrpcServerApplication { + + public static void main(String[] args) { + SpringApplication.run(GrpcServerApplication.class, args); + } + + @Bean + public ServletRegistrationBean grpcServlet(List services) { + List paths = services.stream() + .map(service -> "/" + service.bindService().getServiceDescriptor().getName() + "/*") + .collect(Collectors.toList()); + ServletRegistrationBean servlet = new ServletRegistrationBean<>(new GrpcServlet(services)); + servlet.setUrlMappings(paths); + return servlet; + } + + @Bean + public BindableService serverReflection() { + return ProtoReflectionService.newInstance(); + } + +} diff --git a/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerService.java b/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerService.java new file mode 100644 index 0000000..fdf0ba7 --- /dev/null +++ b/samples/grpc-tomcat/src/main/java/org/springframework/grpc/sample/GrpcServerService.java @@ -0,0 +1,47 @@ +package org.springframework.grpc.sample; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.grpc.sample.proto.HelloReply; +import org.springframework.grpc.sample.proto.HelloRequest; +import org.springframework.grpc.sample.proto.SimpleGrpc; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import io.grpc.stub.StreamObserver; + +@Service +public class GrpcServerService extends SimpleGrpc.SimpleImplBase { + + private static Log log = LogFactory.getLog(GrpcServerService.class); + + @Override + public void sayHello(HelloRequest req, StreamObserver responseObserver) { + log.info("Hello " + req.getName()); + HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + req.getName()).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + @Async + public void streamHello(HelloRequest req, StreamObserver responseObserver) { + log.info("Hello " + req.getName()); + int count = 0; + while (count < 10) { + HelloReply reply = HelloReply.newBuilder().setMessage("Hello(" + count + ") ==> " + req.getName()).build(); + responseObserver.onNext(reply); + count++; + try { + Thread.sleep(1000L); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + responseObserver.onError(e); + return; + } + } + responseObserver.onCompleted(); + } + +} \ No newline at end of file diff --git a/samples/grpc-tomcat/src/main/proto/hello.proto b/samples/grpc-tomcat/src/main/proto/hello.proto new file mode 100644 index 0000000..731679c --- /dev/null +++ b/samples/grpc-tomcat/src/main/proto/hello.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.springframework.grpc.sample.proto"; +option java_outer_classname = "HelloWorldProto"; + +// The greeting service definition. +service Simple { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) { + } + rpc StreamHello(HelloRequest) returns (stream HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/samples/grpc-tomcat/src/main/resources/META-INF/native-image/org.springframework.samples/grpc-tomcat-sample/native-image.properties b/samples/grpc-tomcat/src/main/resources/META-INF/native-image/org.springframework.samples/grpc-tomcat-sample/native-image.properties new file mode 100644 index 0000000..5c43451 --- /dev/null +++ b/samples/grpc-tomcat/src/main/resources/META-INF/native-image/org.springframework.samples/grpc-tomcat-sample/native-image.properties @@ -0,0 +1,2 @@ +# Ignored unless building in Nix (https://github.com/oracle/graal/issues/8639) +Args = -ENIX_LDFLAGS -ENIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu diff --git a/samples/grpc-tomcat/src/main/resources/application.properties b/samples/grpc-tomcat/src/main/resources/application.properties new file mode 100644 index 0000000..5f753c5 --- /dev/null +++ b/samples/grpc-tomcat/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.application.name=grpc-tomcat +server.port=9090 +server.http2.enabled=true \ No newline at end of file diff --git a/samples/grpc-tomcat/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java b/samples/grpc-tomcat/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java new file mode 100644 index 0000000..7849926 --- /dev/null +++ b/samples/grpc-tomcat/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java @@ -0,0 +1,57 @@ +package org.springframework.grpc.sample; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +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; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; +import org.springframework.grpc.client.GrpcChannelFactory; +import org.springframework.grpc.sample.proto.HelloReply; +import org.springframework.grpc.sample.proto.HelloRequest; +import org.springframework.grpc.sample.proto.SimpleGrpc; +import org.springframework.test.annotation.DirtiesContext; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GrpcServerApplicationTests { + + private static Log log = LogFactory.getLog(GrpcServerApplicationTests.class); + + public static void main(String[] args) { + new SpringApplicationBuilder(GrpcServerApplication.class, ExtraConfiguration.class).profiles("ssl").run(args); + } + + @Autowired + private SimpleGrpc.SimpleBlockingStub stub; + + @Test + @DirtiesContext + void contextLoads() { + } + + @Test + @DirtiesContext + void serverResponds() { + log.info("Testing"); + HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName("Alien").build()); + assertEquals("Hello ==> Alien", response.getMessage()); + } + + @TestConfiguration + static class ExtraConfiguration { + + @Bean + @Lazy + SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalServerPort int port) { + return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port).build()); + } + + } + +} diff --git a/samples/pom.xml b/samples/pom.xml index 56a037b..1a32918 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -17,6 +17,7 @@ grpc-server + grpc-tomcat @@ -31,4 +32,4 @@ - \ No newline at end of file +