From d87bd54c6c99c8fa272640e7fc073a2f540fbc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20CROCQUESEL?= <88554524+scrocquesel@users.noreply.github.com> Date: Thu, 26 Jan 2023 23:29:18 +0100 Subject: [PATCH] Force java_generic_services to false for protos gathered from dependencies --- .../main/asciidoc/grpc-getting-started.adoc | 4 ++ .../quarkus/grpc/deployment/GrpcCodeGen.java | 41 +++++++++++++++++-- .../proto-with-java_generic_services.proto | 14 +++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 integration-tests/grpc-external-proto/src/main/resources/dir/proto-with-java_generic_services.proto diff --git a/docs/src/main/asciidoc/grpc-getting-started.adoc b/docs/src/main/asciidoc/grpc-getting-started.adoc index 5b70eb64e3c2c..1b4871783bf6b 100644 --- a/docs/src/main/asciidoc/grpc-getting-started.adoc +++ b/docs/src/main/asciidoc/grpc-getting-started.adoc @@ -118,6 +118,8 @@ message HelloReply { This `proto` file defines a simple service interface with a single method (`SayHello`), and the exchanged messages (`HelloRequest` containing the name and `HelloReply` containing the greeting message). +NOTE: Your `proto` file must not contain `option java_generic_services = true;`. https://developers.google.com/protocol-buffers/docs/reference/java-generated?hl=en#service[Generic services are deprecated] and are not compatible with Quarkus code generation plugins. + Before coding, we need to generate the classes used to implement and consume gRPC services. In a terminal, run: @@ -178,6 +180,8 @@ quarkus.generate-code.grpc.scan-for-proto=: ---- The value of the property may be `none`, which is the default value, or a comma separated list of `groupId:artifactId` coordinates. +NOTE: `option java_generic_services = true;` will automatically be removed from proto file containing it. + == Different gRPC implementations / types Another thing to take note as well is that Quarkus' gRPC support currently includes 3 different types of gRPC usage: diff --git a/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java b/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java index febf3dec9e4b1..b08895b926ea5 100644 --- a/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java +++ b/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java @@ -146,6 +146,35 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { return false; } + private static void copySanitizedProtoFile(ResolvedDependency artifact, Path protoPath, Path outProtoPath) + throws IOException { + boolean genericServicesFound = false; + + try (var reader = Files.newBufferedReader(protoPath); + var writer = Files.newBufferedWriter(outProtoPath)) { + + String line = reader.readLine(); + while (line != null) { + // filter java_generic_services to avoid "Tried to write the same file twice" + // when set to true. Generic services are deprecated and replaced by classes generated by + // this plugin + if (!line.contains("java_generic_services")) { + writer.write(line); + writer.newLine(); + } else { + genericServicesFound = true; + } + + line = reader.readLine(); + } + } + + if (genericServicesFound) { + log.infof("Ignoring option java_generic_services in %s:%s%s.", artifact.getGroupId(), artifact.getArtifactId(), + protoPath); + } + } + private void postprocessing(CodeGenContext context, Path outDir) { if (TRUE.toString().equalsIgnoreCase(System.getProperties().getProperty(POST_PROCESS_SKIP, "false")) || context.config().getOptionalValue(POST_PROCESS_SKIP, Boolean.class).orElse(false)) { @@ -179,7 +208,7 @@ private Collection gatherProtosFromDependencies(Path workDir, Set if (scanAll || dependenciesToScan.contains( String.format("%s:%s", artifact.getGroupId(), artifact.getArtifactId()))) { - extractProtosFromArtifact(workDir, protoFilesFromDependencies, protoDirectories, artifact); + extractProtosFromArtifact(workDir, protoFilesFromDependencies, protoDirectories, artifact, true); } } return protoFilesFromDependencies; @@ -215,14 +244,14 @@ private Collection gatherDirectoriesWithImports(Path workDir, CodeGenCon if (scanAll || dependenciesToScan.contains( String.format("%s:%s", artifact.getGroupId(), artifact.getArtifactId()))) { - extractProtosFromArtifact(workDir, new ArrayList<>(), importDirectories, artifact); + extractProtosFromArtifact(workDir, new ArrayList<>(), importDirectories, artifact, false); } } return importDirectories; } private void extractProtosFromArtifact(Path workDir, Collection protoFiles, - Set protoDirectories, ResolvedDependency artifact) throws CodeGenException { + Set protoDirectories, ResolvedDependency artifact, boolean isDependency) throws CodeGenException { try { artifact.getContentTree().walk( @@ -250,7 +279,11 @@ private void extractProtosFromArtifact(Path workDir, Collection protoFiles } try { Files.createDirectories(outPath.getParent()); - Files.copy(path, outPath, StandardCopyOption.REPLACE_EXISTING); + if (isDependency) { + copySanitizedProtoFile(artifact, path, outPath); + } else { + Files.copy(path, outPath, StandardCopyOption.REPLACE_EXISTING); + } protoFiles.add(outPath); } catch (IOException e) { throw new GrpcCodeGenException("Failed to extract proto file" + path + " to target: " diff --git a/integration-tests/grpc-external-proto/src/main/resources/dir/proto-with-java_generic_services.proto b/integration-tests/grpc-external-proto/src/main/resources/dir/proto-with-java_generic_services.proto new file mode 100644 index 0000000000000..a281585092a82 --- /dev/null +++ b/integration-tests/grpc-external-proto/src/main/resources/dir/proto-with-java_generic_services.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +option java_package = "com.test"; + +// codegen will ignore the line below +option java_generic_services = true; + +package com.test; + +service MyJavaGenericServicesTest { + rpc DoTest(JavaGenericServices) returns (JavaGenericServices); +} + +message JavaGenericServices {} \ No newline at end of file