diff --git a/examples/microprofile/grpc/README.md b/examples/microprofile/grpc/README.md
new file mode 100644
index 00000000..4fa6c590
--- /dev/null
+++ b/examples/microprofile/grpc/README.md
@@ -0,0 +1,15 @@
+# Helidon gRPC MP Example
+
+This examples shows a simple application written using Helidon gRPC MP API:
+
+- StringService: a gRPC service implementation that uses MP
+- StringServiceClient: an interface from which a client proxy can be created to call StringService remote methods
+- StringServiceTest: a sample test that starts a server and tests the client and server components
+- application.yaml: configuration for server and client channels
+
+## Build and run
+
+```shell
+mvn package
+java -jar target/helidon-examples-microprofile-grpc.jar
+```
\ No newline at end of file
diff --git a/examples/microprofile/grpc/pom.xml b/examples/microprofile/grpc/pom.xml
new file mode 100644
index 00000000..fcd36e79
--- /dev/null
+++ b/examples/microprofile/grpc/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+ 4.0.0
+
+ io.helidon.applications
+ helidon-mp
+ 4.1.0-SNAPSHOT
+
+
+ io.helidon.examples.microprofile
+ helidon-examples-microprofile-grpc
+ Helidon Examples Microprofile gRPC
+
+
+ Microprofile example that uses gRPC
+
+
+
+
+ io.helidon.grpc
+ helidon-grpc-core
+
+
+ io.helidon.microprofile.grpc
+ helidon-microprofile-grpc-core
+
+
+ io.helidon.microprofile.grpc
+ helidon-microprofile-grpc-server
+
+
+ io.helidon.microprofile.grpc
+ helidon-microprofile-grpc-client
+
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-junit5
+ test
+
+
+ io.helidon.webserver.testing.junit5
+ helidon-webserver-testing-junit5-grpc
+ test
+
+
+ io.helidon.logging
+ helidon-logging-jul
+ runtime
+
+
+
+ javax.annotation
+ javax.annotation-api
+ provided
+ true
+
+
+
+
+
+
+
+ kr.motd.maven
+ os-maven-plugin
+ ${version.plugin.os}
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-libs
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+
+
+
+ compile
+ test-compile
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringService.java b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringService.java
new file mode 100644
index 00000000..046793bd
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringService.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.helidon.examples.microprofile.grpc;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import io.helidon.grpc.api.Grpc;
+import io.helidon.grpc.core.CollectingObserver;
+
+import io.grpc.stub.StreamObserver;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * An implementation of a string service.
+ */
+@Grpc.GrpcService
+@ApplicationScoped
+public class StringService {
+
+ /**
+ * Uppercase a string.
+ *
+ * @param request string message
+ * @return string message
+ */
+ @Grpc.Unary("Upper")
+ public Strings.StringMessage upper(Strings.StringMessage request) {
+ return newMessage(request.getText().toUpperCase());
+ }
+
+ /**
+ * Lowercase a string.
+ *
+ * @param request string message
+ * @return string message
+ */
+ @Grpc.Unary("Lower")
+ public Strings.StringMessage lower(Strings.StringMessage request) {
+ return newMessage(request.getText().toLowerCase());
+ }
+
+ /**
+ * Split a string using space delimiters.
+ *
+ * @param request string message
+ * @return stream of string messages
+ */
+ @Grpc.ServerStreaming("Split")
+ public Stream split(Strings.StringMessage request) {
+ String[] parts = request.getText().split(" ");
+ return Stream.of(parts).map(this::newMessage);
+ }
+
+ /**
+ * Join a stream of messages using spaces.
+ *
+ * @param observer stream of messages
+ * @return single message as a stream
+ */
+ @Grpc.ClientStreaming("Join")
+ public StreamObserver join(StreamObserver observer) {
+ return CollectingObserver.create(
+ Collectors.joining(" "),
+ observer,
+ Strings.StringMessage::getText,
+ this::newMessage);
+ }
+
+ private Strings.StringMessage newMessage(String text) {
+ return Strings.StringMessage.newBuilder().setText(text).build();
+ }
+}
+
diff --git a/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringServiceClient.java b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringServiceClient.java
new file mode 100644
index 00000000..b03ee3e9
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/StringServiceClient.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.helidon.examples.microprofile.grpc;
+
+import java.util.stream.Stream;
+
+import io.helidon.grpc.api.Grpc;
+
+import io.grpc.stub.StreamObserver;
+
+/**
+ * A client for a {@link StringService}.
+ */
+@Grpc.GrpcService("StringService")
+@Grpc.GrpcChannel("string-channel") // see application.yaml
+public interface StringServiceClient {
+
+ /**
+ * Uppercase a string.
+ *
+ * @param request string message
+ * @return string message
+ */
+ @Grpc.Unary("Upper")
+ Strings.StringMessage upper(Strings.StringMessage request);
+
+ /**
+ * Lowercase a string.
+ *
+ * @param request string message
+ * @return string message
+ */
+ @Grpc.Unary("Lower")
+ Strings.StringMessage lower(Strings.StringMessage request);
+
+ /**
+ * Split a string using space delimiters.
+ *
+ * @param request string message
+ * @return stream of string messages
+ */
+ @Grpc.ServerStreaming("Split")
+ Stream split(Strings.StringMessage request);
+
+ /**
+ * Join a stream of messages using spaces.
+ *
+ * @param observer stream of messages
+ * @return single message as a stream
+ */
+ @Grpc.ClientStreaming("Join")
+ StreamObserver join(StreamObserver observer);
+}
+
diff --git a/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/package-info.java b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/package-info.java
new file mode 100644
index 00000000..fbf691b8
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/java/io/helidon/examples/microprofile/grpc/package-info.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+package io.helidon.examples.microprofile.grpc;
diff --git a/examples/microprofile/grpc/src/main/proto/strings.proto b/examples/microprofile/grpc/src/main/proto/strings.proto
new file mode 100644
index 00000000..48c8bd30
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/proto/strings.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+
+
+syntax = "proto3";
+option java_package = "io.helidon.examples.microprofile.grpc";
+
+service StringService {
+ rpc Upper (StringMessage) returns (StringMessage) {}
+ rpc Lower (StringMessage) returns (StringMessage) {}
+ rpc Split (StringMessage) returns (stream StringMessage) {}
+ rpc Join (stream StringMessage) returns (StringMessage) {}
+}
+
+message StringMessage {
+ string text = 1;
+}
diff --git a/examples/microprofile/grpc/src/main/resources/META-INF/beans.xml b/examples/microprofile/grpc/src/main/resources/META-INF/beans.xml
new file mode 100644
index 00000000..69f490f1
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/examples/microprofile/grpc/src/main/resources/application.yaml b/examples/microprofile/grpc/src/main/resources/application.yaml
new file mode 100644
index 00000000..fdc488cf
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/resources/application.yaml
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2024 Oracle and/or its affiliates.
+#
+# 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.
+#
+
+server:
+ port: 0
+ tls:
+ trust:
+ keystore:
+ passphrase: "password"
+ trust-store: true
+ resource:
+ resource-path: "server.p12"
+ private-key:
+ keystore:
+ passphrase: "password"
+ resource:
+ resource-path: "server.p12"
+
+grpc:
+ client:
+ channels:
+ - name: "string-channel"
+ port: 0
+ tls:
+ trust:
+ keystore:
+ passphrase: "password"
+ trust-store: true
+ resource:
+ resource-path: "client.p12"
+ private-key:
+ keystore:
+ passphrase: "password"
+ resource:
+ resource-path: "client.p12"
\ No newline at end of file
diff --git a/examples/microprofile/grpc/src/main/resources/client.p12 b/examples/microprofile/grpc/src/main/resources/client.p12
new file mode 100644
index 00000000..4eb3b832
Binary files /dev/null and b/examples/microprofile/grpc/src/main/resources/client.p12 differ
diff --git a/examples/microprofile/grpc/src/main/resources/logging.properties b/examples/microprofile/grpc/src/main/resources/logging.properties
new file mode 100644
index 00000000..10379e42
--- /dev/null
+++ b/examples/microprofile/grpc/src/main/resources/logging.properties
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2019, 2024 Oracle and/or its affiliates.
+#
+# 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.
+#
+
+# Example Logging Configuration File
+# For more information see $JAVA_HOME/jre/lib/logging.properties
+
+# Send messages to the console
+handlers=io.helidon.logging.jul.HelidonConsoleHandler
+
+# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
+java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n
+
+# Global logging level. Can be overridden by specific loggers
+.level=INFO
+
+
diff --git a/examples/microprofile/grpc/src/main/resources/server.p12 b/examples/microprofile/grpc/src/main/resources/server.p12
new file mode 100644
index 00000000..ff8e4ddf
Binary files /dev/null and b/examples/microprofile/grpc/src/main/resources/server.p12 differ
diff --git a/examples/microprofile/grpc/src/test/java/io/helidon/examples/microprofile/grpc/StringServiceTest.java b/examples/microprofile/grpc/src/test/java/io/helidon/examples/microprofile/grpc/StringServiceTest.java
new file mode 100644
index 00000000..b6e9410f
--- /dev/null
+++ b/examples/microprofile/grpc/src/test/java/io/helidon/examples/microprofile/grpc/StringServiceTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+package io.helidon.examples.microprofile.grpc;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import io.helidon.grpc.api.Grpc;
+import io.helidon.microprofile.grpc.client.GrpcConfigurablePort;
+import io.helidon.microprofile.testing.junit5.HelidonTest;
+
+import io.grpc.stub.StreamObserver;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.WebTarget;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasSize;
+
+@HelidonTest
+class StringServiceTest {
+
+ @Inject
+ private WebTarget webTarget;
+
+ @Inject
+ @Grpc.GrpcProxy
+ private StringServiceClient client;
+
+ @BeforeEach
+ void updatePort() {
+ if (client instanceof GrpcConfigurablePort c) {
+ c.channelPort(webTarget.getUri().getPort());
+ }
+ }
+
+ @Test
+ void testUnaryUpper() {
+ Strings.StringMessage res = client.upper(newMessage("hello"));
+ assertThat(res.getText(), is("HELLO"));
+ }
+
+ @Test
+ void testUnaryLower() {
+ Strings.StringMessage res = client.lower(newMessage("HELLO"));
+ assertThat(res.getText(), is("hello"));
+ }
+
+ @Test
+ void testServerStreamingSplit() {
+ Stream stream = client.split(newMessage("hello world"));
+ List value = stream.toList();
+ assertThat(value, hasSize(2));
+ assertThat(value, contains(newMessage("hello"), newMessage("world")));
+ }
+
+ @Test
+ void testClientStreamingJoin() throws InterruptedException {
+ ListObserver response = new ListObserver<>();
+ StreamObserver request = client.join(response);
+ request.onNext(newMessage("hello"));
+ request.onNext(newMessage("world"));
+ request.onCompleted();
+ List value = response.value();
+ assertThat(value.getFirst(), is(newMessage("hello world")));
+ }
+
+ /**
+ * Helper method to create a string message from a string.
+ *
+ * @param data the string
+ * @return the string message
+ */
+ Strings.StringMessage newMessage(String data) {
+ return Strings.StringMessage.newBuilder().setText(data).build();
+ }
+
+ /**
+ * Helper class to collect a list of observed values.
+ *
+ * @param the type of values
+ */
+ static class ListObserver implements StreamObserver {
+ private static final long TIMEOUT_SECONDS = 10;
+
+ private List value = new ArrayList<>();
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ public List value() throws InterruptedException {
+ boolean b = latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assert b;
+ return value;
+ }
+
+ @Override
+ public void onNext(T value) {
+ this.value.add(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ value = null;
+ }
+
+ @Override
+ public void onCompleted() {
+ latch.countDown();
+ }
+ }
+}
+
diff --git a/examples/microprofile/pom.xml b/examples/microprofile/pom.xml
index db26994f..fd4c017e 100644
--- a/examples/microprofile/pom.xml
+++ b/examples/microprofile/pom.xml
@@ -52,6 +52,7 @@
http-status-count-mp
lra
telemetry
+ grpc
threads