diff --git a/dotnet/src/Skills/Skills.Grpc/GrpcOperationRunner.cs b/dotnet/src/Skills/Skills.Grpc/GrpcOperationRunner.cs
index bdca0c7ff558..621ec6719835 100644
--- a/dotnet/src/Skills/Skills.Grpc/GrpcOperationRunner.cs
+++ b/dotnet/src/Skills/Skills.Grpc/GrpcOperationRunner.cs
@@ -160,7 +160,7 @@ private object GenerateOperationRequest(GrpcOperation operation, Type type, IDic
throw new GrpcOperationException($"No '{GrpcOperation.PayloadArgumentName}' argument representing gRPC request message is found for the '{operation.Name}' gRPC operation.");
}
- //Deserializing JOSN payload to gRPC request message
+ //Deserializing JSON payload to gRPC request message
var instance = JsonSerializer.Deserialize(payload, type, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (instance == null)
{
@@ -212,7 +212,7 @@ private static Type BuildGrpcOperationDataContractType(GrpcOperationDataContract
setterIl.Emit(OpCodes.Stfld, fieldBuilder);
setterIl.Emit(OpCodes.Ret);
- //Registering the property get and set methods.
+ //Registering the property get and set methods.
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
diff --git a/samples/java/JavaReferenceSkill/.gitignore b/samples/java/JavaReferenceSkill/.gitignore
new file mode 100644
index 000000000000..fc3f89ced511
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/.gitignore
@@ -0,0 +1,39 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/samples/java/JavaReferenceSkill/README.md b/samples/java/JavaReferenceSkill/README.md
new file mode 100644
index 000000000000..8a4306c51baf
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/README.md
@@ -0,0 +1,23 @@
+# Java Reference Skill gRPC Server
+This is a sample Java gRPC server that can be invoked via SK's gRPC client as a Native Skill/Function. The purpose of this project is to demonstrate how Polyglot skills can be supported using either REST or gRPC.
+
+## Prerequisites
+* Java 17
+* Maven
+
+## Build
+To build the project, run the following command:
+```
+mvn clean package
+```
+To generate the gRPC classes, run the following command:
+```
+mvn protobuf:compile
+```
+
+## Run
+To run the project, run the following command:
+```
+java -jar ./target/JavaReferenceSkill-1.0-SNAPSHOT-jar-with-dependencies.jar
+```
+
diff --git a/samples/java/JavaReferenceSkill/pom.xml b/samples/java/JavaReferenceSkill/pom.xml
new file mode 100644
index 000000000000..9161cfc1cbe1
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/pom.xml
@@ -0,0 +1,109 @@
+
+
+ 4.0.0
+
+ com.microsoft.semantickernel.skills.random
+ JavaReferenceSkill
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+ 1.54.0
+ 1.2
+ 1.7.1
+ 0.6.1
+ 3.22.2
+ 5.2.0
+
+
+
+
+ io.grpc
+ grpc-protobuf
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-stub
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-testing
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-netty-shaded
+ ${grpc.version}
+
+
+ org.mockito
+ mockito-core
+ ${mockito-core.version}
+
+
+ javax.annotation
+ javax.annotation-api
+ ${javax.annotation-api.version}
+
+
+
+
+
+
+ kr.motd.maven
+ os-maven-plugin
+ ${os-maven-plugin.version}
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+ ${protobuf-maven-plugin.version}
+
+ com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
+ grpc-java
+ io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
+
+
+
+
+ compile
+ compile-custom
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ jar-with-dependencies
+
+
+
+ com.microsoft.semantickernel.skills.random.Main
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/Main.java b/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/Main.java
new file mode 100644
index 000000000000..6719a9aefb59
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/Main.java
@@ -0,0 +1,28 @@
+package com.microsoft.semantickernel.skills.random;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+
+import java.util.logging.Logger;
+
+public class Main {
+
+ private static final int PORT = 50051;
+
+ public static void main(String[] args) {
+ Logger logger = java.util.logging.Logger.getLogger(Main.class.getName());
+
+ Server server = ServerBuilder.forPort(PORT)
+ .addService(new RandomActivitySkill()).build();
+
+ System.out.println("Starting server...");
+ try {
+ server.start();
+ System.out.println("gRPC Server for random activity started on port " + PORT);
+ server.awaitTermination();
+ } catch (Exception e) {
+ logger.severe("Error with request: " + e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/RandomActivitySkill.java b/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/RandomActivitySkill.java
new file mode 100644
index 000000000000..7036a2dc8976
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/src/main/java/com/microsoft/semantickernel/skills/random/RandomActivitySkill.java
@@ -0,0 +1,42 @@
+package com.microsoft.semantickernel.skills.random;
+
+import io.grpc.stub.StreamObserver;
+import reference_skill.ActivityOuterClass;
+import reference_skill.RandomActivitySkillGrpc;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+
+public class RandomActivitySkill extends RandomActivitySkillGrpc.RandomActivitySkillImplBase {
+
+ public static final String API_ACTIVITY_URL = "https://www.boredapi.com/api/activity";
+
+ /**
+ *
+ * GetRandomActivity is an RPC method that retrieves a random activity from an API.
+ *
+ *
+ * @param request
+ * @param responseObserver
+ */
+ @Override
+ public void getRandomActivity(ActivityOuterClass.GetRandomActivityRequest request, StreamObserver responseObserver) {
+ Logger logger = java.util.logging.Logger.getLogger(this.getClass().getName());
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest httpRequest = HttpRequest.newBuilder()
+ .uri(URI.create(API_ACTIVITY_URL))
+ .build();
+ try {
+ CompletableFuture> response = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString());
+ logger.info("Response: " + response.get().body());
+ responseObserver.onNext(ActivityOuterClass.GetRandomActivityResponse.newBuilder().setActivity(response.get().body()).build());
+ responseObserver.onCompleted();
+ } catch (Exception e) {
+ logger.severe("Error with request: " + e.getMessage());
+ }
+ }
+}
diff --git a/samples/java/JavaReferenceSkill/src/main/proto/activity.proto b/samples/java/JavaReferenceSkill/src/main/proto/activity.proto
new file mode 100644
index 000000000000..ac09fb2b676f
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/src/main/proto/activity.proto
@@ -0,0 +1,30 @@
+syntax = "proto3";
+
+package reference_skill;
+
+// GetRandomActivityRequest is a message that contains input for the GetRandomActivity RPC method.
+message GetRandomActivityRequest {
+ string input = 1; // Input is a hobby that is use to generate a random activity.
+}
+
+// GetRandomActivityResponse is a message that contains the activity returned by the GetRandomActivity RPC method.
+message GetRandomActivityResponse {
+ string activity = 1; // Activity is a description of the random activity.
+}
+
+// RandomActivitySkill is a service that provides methods related to random activities.
+service RandomActivitySkill {
+ // GetRandomActivity is an RPC method that retrieves a random activity from an API.
+ rpc GetRandomActivity (GetRandomActivityRequest) returns (GetRandomActivityResponse);
+}
+
+// Activity is a message that represents an activity with its various properties.
+message Activity {
+ string activity = 1; // A description of the activity.
+ string type = 2; // The type or category of the activity.
+ int32 participants = 3; // The number of participants required for the activity.
+ double price = 4; // The cost associated with the activity, from 0 (free) to 1 (most expensive).
+ string link = 5; // A URL providing more information about the activity.
+ string key = 6; // A unique identifier for the activity.
+ float accessibility = 7; // The accessibility of the activity, from 0 (most accessible) to 1 (least accessible).
+}
diff --git a/samples/java/JavaReferenceSkill/src/test/java/com/microsoft/semantickernel/skills/random/RandomActivitySkillTest.java b/samples/java/JavaReferenceSkill/src/test/java/com/microsoft/semantickernel/skills/random/RandomActivitySkillTest.java
new file mode 100644
index 000000000000..fdc8f7268e24
--- /dev/null
+++ b/samples/java/JavaReferenceSkill/src/test/java/com/microsoft/semantickernel/skills/random/RandomActivitySkillTest.java
@@ -0,0 +1,51 @@
+package com.microsoft.semantickernel.skills.random;
+
+import io.grpc.stub.StreamObserver;
+import io.grpc.testing.GrpcServerRule;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import reference_skill.ActivityOuterClass;
+import reference_skill.RandomActivitySkillGrpc;
+
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.concurrent.CompletableFuture;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+public class RandomActivitySkillTest {
+
+ @Rule
+ public GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();
+
+ private RandomActivitySkillGrpc.RandomActivitySkillBlockingStub blockingStub;
+
+ @Before
+ public void setUp() {
+ grpcServerRule.getServiceRegistry().addService(new RandomActivitySkill());
+ blockingStub = RandomActivitySkillGrpc.newBlockingStub(grpcServerRule.getChannel());
+ }
+
+ @Test
+ public void testGetRandomActivity() throws Exception {
+ HttpClient httpClient = mock(HttpClient.class);
+ HttpResponse httpResponse = mock(HttpResponse.class);
+ CompletableFuture> responseFuture = CompletableFuture.completedFuture(httpResponse);
+
+ when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandler.class))).thenReturn(responseFuture);
+ when(httpResponse.body()).thenReturn("{\"activity\":\"Test Activity\"}");
+
+ RandomActivitySkill randomActivitySkill = new RandomActivitySkill() {
+ };
+
+ ActivityOuterClass.GetRandomActivityRequest request = ActivityOuterClass.GetRandomActivityRequest.newBuilder().build();
+ StreamObserver responseObserver = mock(StreamObserver.class);
+ randomActivitySkill.getRandomActivity(request, responseObserver);
+
+ verify(responseObserver).onNext(any(ActivityOuterClass.GetRandomActivityResponse.class));
+ verify(responseObserver).onCompleted();
+ }
+}