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
+