You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2023/08/15 15:08:09 UTC
[camel-quarkus] branch main updated: Introduce custom CodeGenProvider for gRPC extension
This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 6e96ceb6a9 Introduce custom CodeGenProvider for gRPC extension
6e96ceb6a9 is described below
commit 6e96ceb6a9dec6d46e7c3fb6ff9743edc3033149
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Aug 15 08:44:10 2023 +0100
Introduce custom CodeGenProvider for gRPC extension
Co-authored-by: Peter Palaga <pp...@redhat.com>
---
docs/antora.yml | 2 -
.../ROOT/pages/reference/extensions/grpc.adoc | 141 ++++---
extensions/grpc/codegen/pom.xml | 150 +++++++
.../codegen/CamelQuarkusGrpcCodegenProvider.java | 434 +++++++++++++++++++++
.../codegen/CamelQuarkusGrpcPostProcessor.java | 90 +++++
.../services/io.quarkus.deployment.CodeGenProvider | 1 +
extensions/grpc/deployment/pom.xml | 4 +
extensions/grpc/pom.xml | 1 +
extensions/grpc/runtime/src/main/doc/usage.adoc | 86 ++--
.../quarkus/grpc/runtime/GrpcBuildTimeConfig.java | 86 ++++
integration-tests-support/grpc/pom.xml | 31 ++
.../main/resources/org/acme/proto/a/proto-a.proto | 35 ++
.../main/resources/org/acme/proto/b/proto-b.proto | 35 ++
.../resources/org/acme/proto/c/proto-c-1.proto | 35 ++
.../org/acme/proto/c/sub/package/proto-c-3.proto | 35 ++
.../resources/org/acme/proto/c/sub/proto-c-2.proto | 35 ++
.../resources/org/acme/proto/d/proto-d-1.proto | 35 ++
.../org/acme/proto/d/sub/package/proto-d-3.proto | 35 ++
.../resources/org/acme/proto/d/sub/proto-d-2.proto | 35 ++
.../main/resources/org/acme/proto/e/common.proto | 35 ++
.../main/resources/org/acme/proto/e/proto-e.proto | 19 +
integration-tests-support/pom.xml | 1 +
integration-tests/grpc/pom.xml | 119 +-----
.../grpc/src/main/proto/pingpong.proto | 3 +
.../grpc/src/main/resources/application.properties | 8 +
.../camel/quarkus/component/grpc/it/GrpcTest.java | 36 ++
pom.xml | 2 -
poms/bom-test/pom.xml | 5 +
poms/bom/pom.xml | 5 +
poms/bom/src/main/generated/flattened-full-pom.xml | 5 +
.../src/main/generated/flattened-reduced-pom.xml | 5 +
.../generated/flattened-reduced-verbose-pom.xml | 5 +
poms/build-parent/pom.xml | 10 -
33 files changed, 1355 insertions(+), 209 deletions(-)
diff --git a/docs/antora.yml b/docs/antora.yml
index 365263e3d5..a7659e3b01 100644
--- a/docs/antora.yml
+++ b/docs/antora.yml
@@ -36,8 +36,6 @@ asciidoc:
mapstruct-version: 1.5.5.Final # replace ${mapstruct.version}
min-maven-version: 3.8.2 # replace ${min-maven-version}
target-maven-version: 3.9.3 # replace ${target-maven-version}
- protobuf-maven-plugin-version: 0.6.1 # replace ${protobuf-maven-plugin-version}
- os-maven-plugin-version: 1.7.1 # replace ${os-maven-plugin-version}
# Attributes used in xrefs to other Antora components
cq-camel-components: components
diff --git a/docs/modules/ROOT/pages/reference/extensions/grpc.adoc b/docs/modules/ROOT/pages/reference/extensions/grpc.adoc
index 623c185294..f9f45ba2a3 100644
--- a/docs/modules/ROOT/pages/reference/extensions/grpc.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/grpc.adoc
@@ -50,47 +50,24 @@ endif::[]
[id="extensions-grpc-usage-protobuf-generated-code"]
=== Protobuf generated code
-You can https://github.com/grpc/grpc-java#generated-code[generate] gRPC service stubs with protobuf.
-
-[id="extensions-grpc-usage-protobuf-maven-plugin"]
-==== protobuf-maven-plugin
-
-The `protobuf-maven-plugin` can generate service stubs for `.proto` files. Below is an example configuration for Maven.
+Camel Quarkus gRPC can generate gRPC service stubs for `.proto` files. When using Maven, ensure that you have
+enabled the `generate-code` goals of the `quarkus-maven-plugin` in your project build.
[source,xml]
----
<build>
- <extensions>
- <extension>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>{os-maven-plugin-version}</version>
- </extension>
- </extensions>
-
<plugins>
<plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>{protobuf-maven-plugin-version}</version>
- <configuration>
- <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- </configuration>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-maven-plugin</artifactId>
+ <version>${quarkus.platform.version}</version>
+ <extensions>true</extensions>
<executions>
<execution>
- <id>compile</id>
- <goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- </execution>
- <execution>
- <id>test-compile</id>
<goals>
- <goal>test-compile</goal>
- <goal>test-compile-custom</goal>
+ <goal>build</goal>
+ <goal>generate-code</goal>
+ <goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
@@ -99,24 +76,49 @@ The `protobuf-maven-plugin` can generate service stubs for `.proto` files. Below
</build>
----
-[id="extensions-grpc-usage-generated-code-limitations"]
-==== Generated code limitations
+With this configuration, you can put your service and message definitions into the `src/main/proto` directory and
+the `quarkus-maven-plugin` will generate code from your `.proto` files.
-A limitation of the generated code is that it is annotated with the non Jakarta EE 10 friendly `@javax.annotation.Generated`.
+[id="extensions-grpc-usage-scanning-proto-files-with-imports"]
+==== Scanning `proto` files with imports
-To avoid compilation issues, the `grpc-java` project recommendation is to add a `provided` scoped dependency like this:
+The Protocol Buffers specification provides a way to import proto files. You can control the scope of dependencies to
+scan by adding configuration property `quarkus.camel.grpc.codegen.scan-for-imports` property to `application.properties`.
+The available options are outlined below.
-[source,xml]
+* `all` - Scan all dependencies
+* `none` - Disable dependency scanning. Use only the proto definitions defined in `src/main/proto` or `src/test/proto`
+* `groupId1:artifactId1,groupId2:artifactId2` - Scan only the dependencies matching the `groupId` and `artifactId` list
+
+The default value is `com.google.protobuf:protobuf-java`.
+
+[id="extensions-grpc-usage-scanning-proto-files-from-dependencies"]
+==== Scanning `proto` files from dependencies
+
+If you have proto files shared across multiple dependencies, you can generate gRPC service stubs for them by adding
+configuration property `quarkus.camel.grpc.codegen.scan-for-proto` to `application.properties`.
+
+First add a dependency for the artifact(s) containing proto files to your project. Next, enable proto file
+dependency scanning.
+
+[source,properties]
----
-<dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>annotations-api</artifactId>
- <version>6.0.53</version>
- <scope>provided</scope>
-</dependency>
+quarkus.camel.grpc.codegen.scan-for-proto=org.my.groupId1:my-artifact-id-1,org.my.groupId2:my-artifact-id-2
----
-Alternatively, you can use Maven or Gradle plugins to iterate over the generated code (found at `target/generated-sources` for Maven) and replace all instances of `@javax.annotation.Generated` with `@jakarta.annotation.Generated`.
+It is possible to include / exclude specific proto files from dependency scanning via configuration properties.
+
+The configuration property name suffix is the Maven `groupId` / `artifactId` for the dependency to configure includes / excludes on.
+Paths are relative to the classpath location of the proto files within the dependency. Paths can be an explicit path to a proto file,
+or as glob patterns to include / exclude multiple files.
+
+[source,properties]
+----
+quarkus.camel.grpc.codegen.scan-for-proto-includes."<groupId>\:<artifactId>"=foo/**,bar/**,baz/a-proto.proto
+quarkus.camel.grpc.codegen.scan-for-proto-excludes."<groupId>\:<artifactId>"=foo/private/**,baz/another-proto.proto
+----
+
+NOTE: The `:` character within property keys must be escaped with `\`.
[id="extensions-grpc-usage-accessing-classpath-resources-in-native-mode"]
=== Accessing classpath resources in native mode
@@ -149,3 +151,54 @@ More information about selecting resources for inclusion in the native executabl
At present there is no support for integrating Camel Quarkus gRPC with Quarkus gRPC. If you have both the `camel-quarkus-grpc` and `quarkus-grpc` extension dependency on the classpath, you are likely to encounter problems at build time when compiling your application.
+
+[id="extensions-grpc-additional-camel-quarkus-configuration"]
+== Additional Camel Quarkus configuration
+
+[width="100%",cols="80,5,15",options="header"]
+|===
+| Configuration property | Type | Default
+
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.grpc.codegen.enabled]]`link:#quarkus.camel.grpc.codegen.enabled[quarkus.camel.grpc.codegen.enabled]`
+
+If `true`, Camel Quarkus gRPC code generation is run for .proto files discovered from the `proto` directory, or from dependencies specified in the `scan-for-proto` or `scan-for-imports` options. When `false`, code generation for .proto files is disabled.
+| `boolean`
+| `true`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.grpc.codegen.scan-for-proto]]`link:#quarkus.camel.grpc.codegen.scan-for-proto[quarkus.camel.grpc.codegen.scan-for-proto]`
+
+Camel Quarkus gRPC code generation can scan application dependencies for .proto files to generate Java stubs from them. This property sets the scope of the dependencies to scan. Applicable values:
+
+ - _none_ - default - don't scan dependencies
+ - a comma separated list of _groupId:artifactId_ coordinates to scan
+ - _all_ - scan all dependencies
+| `string`
+| `none`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.grpc.codegen.scan-for-imports]]`link:#quarkus.camel.grpc.codegen.scan-for-imports[quarkus.camel.grpc.codegen.scan-for-imports]`
+
+Camel Quarkus gRPC code generation can scan dependencies for .proto files that can be imported by protos in this applications. Applicable values:
+
+ - _none_ - default - don't scan dependencies
+ - a comma separated list of _groupId:artifactId_ coordinates to scan
+ - _all_ - scan all dependencies The default is _com.google.protobuf:protobuf-java_.
+| `string`
+| `com.google.protobuf:protobuf-java`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.grpc.codegen.scan-for-proto-includes]]`link:#quarkus.camel.grpc.codegen.scan-for-proto-includes[quarkus.camel.grpc.codegen.scan-for-proto-includes]`
+
+Package path or file glob pattern includes per dependency containing .proto files to be considered for inclusion.
+| ``Map<String,List<String>>``
+|
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.grpc.codegen.scan-for-proto-excludes]]`link:#quarkus.camel.grpc.codegen.scan-for-proto-excludes[quarkus.camel.grpc.codegen.scan-for-proto-excludes]`
+
+Package path or file glob pattern includes per dependency containing .proto files to be considered for exclusion.
+| ``Map<String,List<String>>``
+|
+|===
+
+[.configuration-legend]
+{doc-link-icon-lock}[title=Fixed at build time] Configuration property fixed at build time. All other configuration properties are overridable at runtime.
+
diff --git a/extensions/grpc/codegen/pom.xml b/extensions/grpc/codegen/pom.xml
new file mode 100644
index 0000000000..759840ed37
--- /dev/null
+++ b/extensions/grpc/codegen/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-grpc-parent</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>camel-quarkus-grpc-codegen</artifactId>
+ <name>Camel Quarkus :: gRPC :: Codegen</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-core-deployment</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-devtools-utilities</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java-util</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.github.javaparser</groupId>
+ <artifactId>javaparser-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>linux-aarch_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>linux-x86_32</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>linux-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>osx-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>osx-aarch_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>windows-x86_32</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protoc</artifactId>
+ <classifier>windows-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>linux-aarch_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>linux-x86_32</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>linux-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>osx-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>osx-aarch_64</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>windows-x86_32</classifier>
+ <type>exe</type>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>protoc-gen-grpc-java</artifactId>
+ <classifier>windows-x86_64</classifier>
+ <type>exe</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcCodegenProvider.java b/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcCodegenProvider.java
new file mode 100644
index 0000000000..4b8f437182
--- /dev/null
+++ b/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcCodegenProvider.java
@@ -0,0 +1,434 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.grpc.codegen;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import io.quarkus.bootstrap.model.ApplicationModel;
+import io.quarkus.bootstrap.prebuild.CodeGenException;
+import io.quarkus.deployment.CodeGenContext;
+import io.quarkus.deployment.CodeGenProvider;
+import io.quarkus.deployment.util.ProcessUtil;
+import io.quarkus.maven.dependency.ResolvedDependency;
+import io.quarkus.paths.PathFilter;
+import io.quarkus.runtime.util.HashUtil;
+import io.quarkus.utilities.OS;
+import org.eclipse.microprofile.config.Config;
+import org.jboss.logging.Logger;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Custom {@link CodeGenProvider} for Camel Quarkus gRPC. Based on the original Quarkus gRPC implementation:
+ * <p>
+ * <a href=
+ * "https://github.com/quarkusio/quarkus/tree/main/extensions/grpc/codegen">https://github.com/quarkusio/quarkus/tree/main/extensions/grpc/codegen</a>
+ * <p>
+ * The implementation does not include additional logic for features not supported in Camel gRPC, such as integration
+ * with Mutiny and CDI.
+ */
+public class CamelQuarkusGrpcCodegenProvider implements CodeGenProvider {
+ private static final Logger LOG = Logger.getLogger(CamelQuarkusGrpcCodegenProvider.class);
+ private static final String EXE = "exe";
+ private static final String PROTO = ".proto";
+ private static final String PROTOC = "protoc";
+ private static final String PROTOC_GROUP_ID = "com.google.protobuf";
+ private static final String SCAN_DEPENDENCIES_FOR_PROTO = "quarkus.camel.grpc.codegen.scan-for-proto";
+ private static final String SCAN_DEPENDENCIES_FOR_PROTO_INCLUDE_PATTERN = "quarkus.camel.grpc.codegen.scan-for-proto-includes.\"%s\"";
+ private static final String SCAN_DEPENDENCIES_FOR_PROTO_EXCLUDE_PATTERN = "quarkus.camel.grpc.codegen.scan-for-proto-excludes.\"%s\"";
+ private static final String SCAN_FOR_IMPORTS = "quarkus.camel.grpc.codegen.scan-for-imports";
+
+ private Executables executables;
+
+ @Override
+ public String providerId() {
+ return "camel-quarkus-grpc";
+ }
+
+ @Override
+ public String inputExtension() {
+ return "proto";
+ }
+
+ @Override
+ public String inputDirectory() {
+ return "proto";
+ }
+
+ @Override
+ public boolean trigger(CodeGenContext context) throws CodeGenException {
+ final Config config = context.config();
+ if (!config.getValue("quarkus.camel.grpc.codegen.enabled", Boolean.class)) {
+ LOG.info("Skipping " + this.getClass() + " invocation on user's request");
+ return false;
+ }
+
+ Path outDir = context.outDir();
+ Path workDir = context.workDir();
+ Set<String> protoDirs = new HashSet<>();
+
+ try {
+ List<String> protoFiles = new ArrayList<>();
+ if (Files.isDirectory(context.inputDir())) {
+ try (Stream<Path> protoFilesPaths = Files.walk(context.inputDir())) {
+ protoFilesPaths
+ .filter(Files::isRegularFile)
+ .filter(s -> s.toString().endsWith(PROTO))
+ .map(Path::normalize)
+ .map(Path::toAbsolutePath)
+ .map(Path::toString)
+ .forEach(protoFiles::add);
+ protoDirs.add(context.inputDir().normalize().toAbsolutePath().toString());
+ }
+ }
+ Path dirWithProtosFromDependencies = workDir.resolve("protoc-protos-from-dependencies");
+ Collection<Path> protoFilesFromDependencies = gatherProtosFromDependencies(dirWithProtosFromDependencies, protoDirs,
+ context);
+ if (!protoFilesFromDependencies.isEmpty()) {
+ protoFilesFromDependencies.stream()
+ .map(Path::normalize)
+ .map(Path::toAbsolutePath)
+ .map(Path::toString)
+ .forEach(protoFiles::add);
+ }
+
+ if (!protoFiles.isEmpty()) {
+ initExecutables(workDir, context.applicationModel());
+
+ Path protocDependenciesDir = workDir.resolve("protoc-dependencies");
+ Collection<String> protosToImport = gatherDirectoriesWithImports(protocDependenciesDir, context);
+
+ if (!protoFilesFromDependencies.isEmpty() && !protosToImport.isEmpty()) {
+ // Clean up potential duplicates that can exist when scan-for-imports artifacts clash with scan-for-proto configs
+ protoFiles.forEach(file -> {
+ if (!file.contains(context.inputDir().toString())) {
+ String relativeFileName = file.replace(dirWithProtosFromDependencies.toString(), "");
+ Path path = Paths.get(protocDependenciesDir.toAbsolutePath().toString(), relativeFileName);
+ if (Files.exists(path)) {
+ LOG.debugf("Cleaning up duplicate proto file %s", path);
+ try {
+ Files.delete(path);
+ } catch (IOException e) {
+ LOG.errorf("Failed cleaning up duplicate proto file %s", e);
+ }
+ }
+ }
+ });
+ }
+
+ List<String> command = new ArrayList<>();
+ command.add(executables.protoc.toString());
+ for (String protoImportDir : protosToImport) {
+ command.add(String.format("-I=%s", escapeWhitespace(protoImportDir)));
+ }
+ for (String protoDir : protoDirs) {
+ command.add(String.format("-I=%s", escapeWhitespace(protoDir)));
+ }
+
+ command.addAll(asList("--plugin=protoc-gen-grpc=" + executables.grpc,
+ "--grpc_out=" + outDir,
+ "--java_out=" + outDir));
+ command.addAll(protoFiles);
+
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+
+ final Process process = ProcessUtil.launchProcess(processBuilder, context.shouldRedirectIO());
+ int resultCode = process.waitFor();
+ if (resultCode != 0) {
+ throw new CodeGenException("Failed to generate Java classes from proto files: " + protoFiles +
+ " to " + outDir.toAbsolutePath() + " with command " + String.join(" ", command));
+ }
+ new CamelQuarkusGrpcPostProcessor(outDir).process();
+ LOG.info("Successfully finished generating and post-processing sources from proto files");
+ return true;
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new CodeGenException(
+ "Failed to generate java files from proto file in " + context.inputDir().toAbsolutePath(), e);
+ }
+
+ 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 Collection<Path> gatherProtosFromDependencies(Path workDir, Set<String> protoDirectories,
+ CodeGenContext context) throws CodeGenException {
+ if (context.test()) {
+ return Collections.emptyList();
+ }
+ Config properties = context.config();
+ String scanDependencies = properties.getValue(SCAN_DEPENDENCIES_FOR_PROTO, String.class);
+
+ if ("none".equalsIgnoreCase(scanDependencies)) {
+ return Collections.emptyList();
+ }
+ boolean scanAll = "all".equalsIgnoreCase(scanDependencies);
+
+ List<String> dependenciesToScan = asList(scanDependencies.split(","));
+
+ ApplicationModel appModel = context.applicationModel();
+ List<Path> protoFilesFromDependencies = new ArrayList<>();
+ for (ResolvedDependency artifact : appModel.getRuntimeDependencies()) {
+ String packageId = String.format("%s:%s", artifact.getGroupId(), artifact.getArtifactId());
+ Collection<String> includes = properties
+ .getOptionalValues(String.format(SCAN_DEPENDENCIES_FOR_PROTO_INCLUDE_PATTERN, packageId), String.class)
+ .orElse(List.of());
+
+ Collection<String> excludes = properties
+ .getOptionalValues(String.format(SCAN_DEPENDENCIES_FOR_PROTO_EXCLUDE_PATTERN, packageId), String.class)
+ .orElse(List.of());
+
+ if (scanAll
+ || dependenciesToScan.contains(packageId)) {
+ extractProtosFromArtifact(workDir, protoFilesFromDependencies, protoDirectories, artifact, includes, excludes,
+ true);
+ }
+ }
+ return protoFilesFromDependencies;
+ }
+
+ @Override
+ public boolean shouldRun(Path sourceDir, Config config) {
+ return CodeGenProvider.super.shouldRun(sourceDir, config)
+ || isGeneratingFromAppDependenciesEnabled(config);
+ }
+
+ private boolean isGeneratingFromAppDependenciesEnabled(Config config) {
+ return config.getOptionalValue(SCAN_DEPENDENCIES_FOR_PROTO, String.class)
+ .filter(value -> !"none".equals(value)).isPresent();
+ }
+
+ private Collection<String> gatherDirectoriesWithImports(Path workDir, CodeGenContext context) throws CodeGenException {
+ Config properties = context.config();
+
+ String scanForImports = properties.getOptionalValue(SCAN_FOR_IMPORTS, String.class)
+ .orElse("com.google.protobuf:protobuf-java");
+
+ if ("none".equals(scanForImports.toLowerCase(Locale.getDefault()))) {
+ return Collections.emptyList();
+ }
+
+ boolean scanAll = "all".equals(scanForImports.toLowerCase(Locale.getDefault()));
+ List<String> dependenciesToScan = Arrays.asList(scanForImports.split(","));
+
+ Set<String> importDirectories = new HashSet<>();
+ ApplicationModel appModel = context.applicationModel();
+ for (ResolvedDependency artifact : appModel.getRuntimeDependencies()) {
+ if (scanAll
+ || dependenciesToScan.contains(
+ String.format("%s:%s", artifact.getGroupId(), artifact.getArtifactId()))) {
+ extractProtosFromArtifact(workDir, new ArrayList<>(), importDirectories, artifact, List.of(),
+ List.of(), false);
+ }
+ }
+ return importDirectories;
+ }
+
+ private void extractProtosFromArtifact(Path workDir, Collection<Path> protoFiles,
+ Set<String> protoDirectories, ResolvedDependency artifact, Collection<String> filesToInclude,
+ Collection<String> filesToExclude, boolean isDependency) throws CodeGenException {
+
+ try {
+ artifact.getContentTree(new PathFilter(filesToInclude, filesToExclude)).walk(
+ pathVisit -> {
+ Path path = pathVisit.getPath();
+ if (Files.isRegularFile(path) && path.getFileName().toString().endsWith(PROTO)) {
+ Path root = pathVisit.getRoot();
+ if (Files.isDirectory(root)) {
+ protoFiles.add(path);
+ protoDirectories.add(path.getParent().normalize().toAbsolutePath().toString());
+ } else { // archive
+ Path relativePath = path.getRoot().relativize(path);
+ Path protoUnzipDir = workDir
+ .resolve(HashUtil.sha1(root.normalize().toAbsolutePath().toString()))
+ .normalize().toAbsolutePath();
+ try {
+ Files.createDirectories(protoUnzipDir);
+ protoDirectories.add(protoUnzipDir.toString());
+ } catch (IOException e) {
+ throw new GrpcCodeGenException("Failed to create directory: " + protoUnzipDir, e);
+ }
+ Path outPath = protoUnzipDir;
+ for (Path part : relativePath) {
+ outPath = outPath.resolve(part.toString());
+ }
+ try {
+ Files.createDirectories(outPath.getParent());
+ 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: "
+ + outPath, e);
+ }
+ }
+ }
+ });
+ } catch (GrpcCodeGenException e) {
+ throw new CodeGenException(e.getMessage(), e);
+ }
+ }
+
+ private String escapeWhitespace(String path) {
+ if (OS.determineOS() == OS.LINUX) {
+ return path.replace(" ", "\\ ");
+ } else {
+ return path;
+ }
+ }
+
+ private void initExecutables(Path workDir, ApplicationModel model) throws CodeGenException {
+ if (executables == null) {
+ Path protocPath;
+ String protocPathProperty = System.getProperty("quarkus.grpc.protoc-path");
+ String classifier = System.getProperty("quarkus.grpc.protoc-os-classifier", osClassifier());
+ if (protocPathProperty == null) {
+ protocPath = findArtifactPath(model, PROTOC_GROUP_ID, PROTOC, classifier, EXE);
+ } else {
+ protocPath = Paths.get(protocPathProperty);
+ }
+ Path protocExe = makeExecutableFromPath(workDir, PROTOC_GROUP_ID, PROTOC, classifier, "exe", protocPath);
+
+ Path protocGrpcPluginExe = prepareExecutable(workDir, model,
+ "io.grpc", "protoc-gen-grpc-java", classifier, "exe");
+
+ executables = new Executables(protocExe, protocGrpcPluginExe);
+ }
+ }
+
+ private Path prepareExecutable(Path buildDir, ApplicationModel model,
+ String groupId, String artifactId, String classifier, String packaging) throws CodeGenException {
+ Path artifactPath = findArtifactPath(model, groupId, artifactId, classifier, packaging);
+
+ return makeExecutableFromPath(buildDir, groupId, artifactId, classifier, packaging, artifactPath);
+ }
+
+ private Path makeExecutableFromPath(Path buildDir, String groupId, String artifactId, String classifier, String packaging,
+ Path artifactPath) throws CodeGenException {
+ Path exe = buildDir.resolve(String.format("%s-%s-%s-%s", groupId, artifactId, classifier, packaging));
+
+ if (Files.exists(exe)) {
+ return exe;
+ }
+
+ if (artifactPath == null) {
+ String location = String.format("%s:%s:%s:%s", groupId, artifactId, classifier, packaging);
+ throw new CodeGenException("Failed to find " + location + " among dependencies");
+ }
+
+ try {
+ Files.copy(artifactPath, exe, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new CodeGenException("Failed to copy file: " + artifactPath + " to " + exe, e);
+ }
+ if (!exe.toFile().setExecutable(true)) {
+ throw new CodeGenException("Failed to make the file executable: " + exe);
+ }
+ return exe;
+ }
+
+ private static Path findArtifactPath(ApplicationModel model, String groupId, String artifactId, String classifier,
+ String packaging) {
+ Path artifactPath = null;
+
+ for (ResolvedDependency artifact : model.getDependencies()) {
+ if (groupId.equals(artifact.getGroupId())
+ && artifactId.equals(artifact.getArtifactId())
+ && classifier.equals(artifact.getClassifier())
+ && packaging.equals(artifact.getType())) {
+ artifactPath = artifact.getResolvedPaths().getSinglePath();
+ }
+ }
+ return artifactPath;
+ }
+
+ private String osClassifier() throws CodeGenException {
+ String architecture = OS.getArchitecture();
+ switch (OS.determineOS()) {
+ case LINUX:
+ return "linux-" + architecture;
+ case WINDOWS:
+ return "windows-" + architecture;
+ case MAC:
+ return "osx-" + architecture;
+ default:
+ throw new CodeGenException(
+ "Unsupported OS, please use maven plugin instead to generate Java classes from proto files");
+ }
+ }
+
+ private static class Executables {
+ final Path protoc;
+ final Path grpc;
+
+ Executables(Path protoc, Path grpc) {
+ this.protoc = protoc;
+ this.grpc = grpc;
+ }
+ }
+
+ private static class GrpcCodeGenException extends RuntimeException {
+ private GrpcCodeGenException(String message, Exception cause) {
+ super(message, cause);
+ }
+ }
+
+}
diff --git a/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcPostProcessor.java b/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcPostProcessor.java
new file mode 100644
index 0000000000..efd44afebd
--- /dev/null
+++ b/extensions/grpc/codegen/src/main/java/org/apache/camel/quarkus/grpc/codegen/CamelQuarkusGrpcPostProcessor.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.grpc.codegen;
+
+import java.io.File;
+import java.nio.file.Path;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.expr.NormalAnnotationExpr;
+import com.github.javaparser.ast.visitor.ModifierVisitor;
+import com.github.javaparser.ast.visitor.Visitable;
+import com.github.javaparser.utils.SourceRoot;
+import org.jboss.logging.Logger;
+
+/**
+ * Post processor for generated proto classes.
+ */
+public class CamelQuarkusGrpcPostProcessor {
+ private static final Logger LOG = Logger.getLogger(CamelQuarkusGrpcPostProcessor.class);
+ private static final String JAVAX_GENERATED = "javax" + ".annotation.Generated";
+ private static final String JAKARTA_GENERATED = "jakarta.annotation.Generated";
+ private final Path root;
+
+ public CamelQuarkusGrpcPostProcessor(Path root) {
+ this.root = root;
+ }
+
+ public static void main(String[] args) {
+ for (String arg : args) {
+ Path path = new File(arg).toPath();
+ CamelQuarkusGrpcPostProcessor postProcessor = new CamelQuarkusGrpcPostProcessor(path);
+ postProcessor.process();
+ }
+ }
+
+ public void process() {
+ SourceRoot sr = new SourceRoot(root);
+ try {
+ sr.parse("", (localPath, absolutePath, result) -> {
+ if (result.isSuccessful()) {
+ CompilationUnit unit = result.getResult().orElseThrow();
+ if (unit.getPrimaryType().isPresent()) {
+ TypeDeclaration<?> type = unit.getPrimaryType().get();
+ process(unit, type);
+ return SourceRoot.Callback.Result.SAVE;
+ }
+ } else {
+ LOG.errorf(
+ "Unable to parse a protoc generated class, skipping post-processing for this " +
+ "file. Reported problems are %s",
+ result.toString());
+ }
+
+ return SourceRoot.Callback.Result.DONT_SAVE;
+ });
+ } catch (Exception e) {
+ LOG.error("Unable to parse protoc generated classes - skipping gRPC post processing", e);
+ }
+ }
+
+ private void process(CompilationUnit unit, TypeDeclaration<?> primary) {
+ LOG.debugf("Post-processing %s", primary.getFullyQualifiedName().orElse(primary.getNameAsString()));
+ unit.accept(new ModifierVisitor<Void>() {
+ @Override
+ public Visitable visit(NormalAnnotationExpr n, Void arg) {
+ // Replace javax.annotation.Generated with the jakarta equivalent
+ // https://github.com/grpc/grpc-java/issues/9179
+ if (n.getNameAsString().equals(JAVAX_GENERATED)) {
+ n.setName(JAKARTA_GENERATED);
+ }
+ return super.visit(n, arg);
+ }
+ }, null);
+ }
+}
diff --git a/extensions/grpc/codegen/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider b/extensions/grpc/codegen/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider
new file mode 100644
index 0000000000..16ab0e7338
--- /dev/null
+++ b/extensions/grpc/codegen/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider
@@ -0,0 +1 @@
+org.apache.camel.quarkus.grpc.codegen.CamelQuarkusGrpcCodegenProvider
\ No newline at end of file
diff --git a/extensions/grpc/deployment/pom.xml b/extensions/grpc/deployment/pom.xml
index bd471510df..f7a8ef6381 100644
--- a/extensions/grpc/deployment/pom.xml
+++ b/extensions/grpc/deployment/pom.xml
@@ -38,6 +38,10 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core-deployment</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-grpc-codegen</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-grpc</artifactId>
diff --git a/extensions/grpc/pom.xml b/extensions/grpc/pom.xml
index b9958962b6..c8a039cdf0 100644
--- a/extensions/grpc/pom.xml
+++ b/extensions/grpc/pom.xml
@@ -31,6 +31,7 @@
<packaging>pom</packaging>
<modules>
+ <module>codegen</module>
<module>deployment</module>
<module>runtime</module>
</modules>
diff --git a/extensions/grpc/runtime/src/main/doc/usage.adoc b/extensions/grpc/runtime/src/main/doc/usage.adoc
index 1bfc0a2eee..a6f686659c 100644
--- a/extensions/grpc/runtime/src/main/doc/usage.adoc
+++ b/extensions/grpc/runtime/src/main/doc/usage.adoc
@@ -1,45 +1,23 @@
=== Protobuf generated code
-You can https://github.com/grpc/grpc-java#generated-code[generate] gRPC service stubs with protobuf.
-
-==== protobuf-maven-plugin
-
-The `protobuf-maven-plugin` can generate service stubs for `.proto` files. Below is an example configuration for Maven.
+Camel Quarkus gRPC can generate gRPC service stubs for `.proto` files. When using Maven, ensure that you have
+enabled the `generate-code` goals of the `quarkus-maven-plugin` in your project build.
[source,xml]
----
<build>
- <extensions>
- <extension>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>{os-maven-plugin-version}</version>
- </extension>
- </extensions>
-
<plugins>
<plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>{protobuf-maven-plugin-version}</version>
- <configuration>
- <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- </configuration>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-maven-plugin</artifactId>
+ <version>${quarkus.platform.version}</version>
+ <extensions>true</extensions>
<executions>
<execution>
- <id>compile</id>
- <goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- </execution>
- <execution>
- <id>test-compile</id>
<goals>
- <goal>test-compile</goal>
- <goal>test-compile-custom</goal>
+ <goal>build</goal>
+ <goal>generate-code</goal>
+ <goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
@@ -48,23 +26,47 @@ The `protobuf-maven-plugin` can generate service stubs for `.proto` files. Below
</build>
----
-==== Generated code limitations
+With this configuration, you can put your service and message definitions into the `src/main/proto` directory and
+the `quarkus-maven-plugin` will generate code from your `.proto` files.
-A limitation of the generated code is that it is annotated with the non Jakarta EE 10 friendly `@javax.annotation.Generated`.
+==== Scanning `proto` files with imports
-To avoid compilation issues, the `grpc-java` project recommendation is to add a `provided` scoped dependency like this:
+The Protocol Buffers specification provides a way to import proto files. You can control the scope of dependencies to
+scan by adding configuration property `quarkus.camel.grpc.codegen.scan-for-imports` property to `application.properties`.
+The available options are outlined below.
-[source,xml]
+* `all` - Scan all dependencies
+* `none` - Disable dependency scanning. Use only the proto definitions defined in `src/main/proto` or `src/test/proto`
+* `groupId1:artifactId1,groupId2:artifactId2` - Scan only the dependencies matching the `groupId` and `artifactId` list
+
+The default value is `com.google.protobuf:protobuf-java`.
+
+==== Scanning `proto` files from dependencies
+
+If you have proto files shared across multiple dependencies, you can generate gRPC service stubs for them by adding
+configuration property `quarkus.camel.grpc.codegen.scan-for-proto` to `application.properties`.
+
+First add a dependency for the artifact(s) containing proto files to your project. Next, enable proto file
+dependency scanning.
+
+[source,properties]
+----
+quarkus.camel.grpc.codegen.scan-for-proto=org.my.groupId1:my-artifact-id-1,org.my.groupId2:my-artifact-id-2
+----
+
+It is possible to include / exclude specific proto files from dependency scanning via configuration properties.
+
+The configuration property name suffix is the Maven `groupId` / `artifactId` for the dependency to configure includes / excludes on.
+Paths are relative to the classpath location of the proto files within the dependency. Paths can be an explicit path to a proto file,
+or as glob patterns to include / exclude multiple files.
+
+[source,properties]
----
-<dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>annotations-api</artifactId>
- <version>6.0.53</version>
- <scope>provided</scope>
-</dependency>
+quarkus.camel.grpc.codegen.scan-for-proto-includes."<groupId>\:<artifactId>"=foo/**,bar/**,baz/a-proto.proto
+quarkus.camel.grpc.codegen.scan-for-proto-excludes."<groupId>\:<artifactId>"=foo/private/**,baz/another-proto.proto
----
-Alternatively, you can use Maven or Gradle plugins to iterate over the generated code (found at `target/generated-sources` for Maven) and replace all instances of `@javax.annotation.Generated` with `@jakarta.annotation.Generated`.
+NOTE: The `:` character within property keys must be escaped with `\`.
=== Accessing classpath resources in native mode
diff --git a/extensions/grpc/runtime/src/main/java/org/apache/camel/quarkus/grpc/runtime/GrpcBuildTimeConfig.java b/extensions/grpc/runtime/src/main/java/org/apache/camel/quarkus/grpc/runtime/GrpcBuildTimeConfig.java
new file mode 100644
index 0000000000..0f7a3eda56
--- /dev/null
+++ b/extensions/grpc/runtime/src/main/java/org/apache/camel/quarkus/grpc/runtime/GrpcBuildTimeConfig.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.grpc.runtime;
+
+import java.util.List;
+import java.util.Map;
+
+import io.quarkus.runtime.annotations.ConfigGroup;
+import io.quarkus.runtime.annotations.ConfigItem;
+import io.quarkus.runtime.annotations.ConfigPhase;
+import io.quarkus.runtime.annotations.ConfigRoot;
+
+@ConfigRoot(name = "camel.grpc", phase = ConfigPhase.BUILD_TIME)
+public class GrpcBuildTimeConfig {
+ /**
+ * Build time configuration options for Camel Quarkus gRPC code generator.
+ */
+ @ConfigItem
+ public CodeGenConfig codegen;
+
+ @ConfigGroup
+ public static class CodeGenConfig {
+ /**
+ * If {@code true}, Camel Quarkus gRPC code generation is run for .proto files discovered from the {@code proto}
+ * directory, or from dependencies specified in the {@code scan-for-proto} or {@code scan-for-imports} options. When
+ * {@code false}, code generation for .proto files is disabled.
+ */
+ @ConfigItem(defaultValue = "true")
+ public boolean enabled;
+
+ /**
+ * Camel Quarkus gRPC code generation can scan application dependencies for .proto files to generate Java stubs
+ * from them.
+ * This property sets the scope of the dependencies to scan.
+ * Applicable values:
+ * <ul>
+ * <li><i>none</i> - default - don't scan dependencies</li>
+ * <li>a comma separated list of <i>groupId:artifactId</i> coordinates to scan</li>
+ * <li><i>all</i> - scan all dependencies</li>
+ * </ul>
+ */
+ @ConfigItem(defaultValue = "none")
+ public String scanForProto;
+
+ /**
+ * Camel Quarkus gRPC code generation can scan dependencies for .proto files that can be imported by protos in this
+ * applications.
+ * Applicable values:
+ * <ul>
+ * <li><i>none</i> - default - don't scan dependencies</li>
+ * <li>a comma separated list of <i>groupId:artifactId</i> coordinates to scan</li>
+ * <li><i>all</i> - scan all dependencies</li>
+ * </ul>
+ *
+ * The default is <i>com.google.protobuf:protobuf-java</i>.
+ */
+ @ConfigItem(defaultValue = "com.google.protobuf:protobuf-java")
+ public String scanForImports;
+
+ /**
+ * Package path or file glob pattern includes per dependency containing .proto files to be considered for inclusion.
+ */
+ @ConfigItem
+ public Map<String, List<String>> scanForProtoIncludes;
+
+ /**
+ * Package path or file glob pattern includes per dependency containing .proto files to be considered for exclusion.
+ */
+ @ConfigItem
+ public Map<String, List<String>> scanForProtoExcludes;
+ }
+}
diff --git a/integration-tests-support/grpc/pom.xml b/integration-tests-support/grpc/pom.xml
new file mode 100644
index 0000000000..ed38040e5c
--- /dev/null
+++ b/integration-tests-support/grpc/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-integration-tests-support</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>camel-quarkus-integration-tests-support-grpc</artifactId>
+ <name>Camel Quarkus :: Integration Tests :: gRPC :: Support</name>
+</project>
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/a/proto-a.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/a/proto-a.proto
new file mode 100644
index 0000000000..f9fc661b41
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/a/proto-a.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.a";
+option java_outer_classname = "PingPongProtoA";
+
+package org.acme.proto.a;
+
+service TestA {
+ rpc PingSyncSync (PingRequestA) returns (PongResponseA) {}
+}
+
+message PingRequestA {
+ string ping_name = 1;
+}
+
+message PongResponseA {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/b/proto-b.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/b/proto-b.proto
new file mode 100644
index 0000000000..4aae4b2a34
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/b/proto-b.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.b";
+option java_outer_classname = "PingPongProtoB";
+
+package org.acme.proto.b;
+
+service TestB {
+ rpc PingSyncSync (PingRequestB) returns (PongResponseB) {}
+}
+
+message PingRequestB {
+ string ping_name = 1;
+}
+
+message PongResponseB {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/proto-c-1.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/proto-c-1.proto
new file mode 100644
index 0000000000..6d0c48f7da
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/proto-c-1.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.c";
+option java_outer_classname = "PingPongProtoC";
+
+package org.acme.proto.c;
+
+service TestC {
+ rpc PingSyncSync (PingRequestC) returns (PongResponseC) {}
+}
+
+message PingRequestC {
+ string ping_name = 1;
+}
+
+message PongResponseC {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/package/proto-c-3.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/package/proto-c-3.proto
new file mode 100644
index 0000000000..7a023479cc
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/package/proto-c-3.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.c.sub.dir";
+option java_outer_classname = "PingPongProtoC";
+
+package org.acme.proto.c.sub.dir;
+
+service TestC {
+ rpc PingSyncSync (PingRequestC) returns (PongResponseC) {}
+}
+
+message PingRequestC {
+ string ping_name = 1;
+}
+
+message PongResponseC {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/proto-c-2.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/proto-c-2.proto
new file mode 100644
index 0000000000..533c40ca33
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/c/sub/proto-c-2.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.c.sub";
+option java_outer_classname = "PingPongProtoC";
+
+package org.acme.proto.c.sub;
+
+service TestC {
+ rpc PingSyncSync (PingRequestC) returns (PongResponseC) {}
+}
+
+message PingRequestC {
+ string ping_name = 1;
+}
+
+message PongResponseC {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/proto-d-1.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/proto-d-1.proto
new file mode 100644
index 0000000000..49b2d57cc3
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/proto-d-1.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.d";
+option java_outer_classname = "PingPongProtoD";
+
+package org.acme.proto.d;
+
+service TestD {
+ rpc PingSyncSync (PingRequestD) returns (PongResponseD) {}
+}
+
+message PingRequestD {
+ string ping_name = 1;
+}
+
+message PongResponseD {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/package/proto-d-3.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/package/proto-d-3.proto
new file mode 100644
index 0000000000..234b487c24
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/package/proto-d-3.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.d.sub.dir";
+option java_outer_classname = "PingPongProtoD";
+
+package org.acme.proto.sub.dir.d;
+
+service TestD {
+ rpc PingSyncSync (PingRequestD) returns (PongResponseD) {}
+}
+
+message PingRequestD {
+ string ping_name = 1;
+}
+
+message PongResponseD {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/proto-d-2.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/proto-d-2.proto
new file mode 100644
index 0000000000..2cf0e8715b
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/d/sub/proto-d-2.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.c.sub";
+option java_outer_classname = "PingPongProtoD";
+
+package org.acme.proto.d.sub;
+
+service TestD {
+ rpc PingSyncSync (PingRequestD) returns (PongResponseD) {}
+}
+
+message PingRequestD {
+ string ping_name = 1;
+}
+
+message PongResponseD {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/common.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/common.proto
new file mode 100644
index 0000000000..9daa38a04b
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/common.proto
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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_multiple_files = true;
+option java_package = "org.acme.proto.e";
+option java_outer_classname = "PingPongProtoE";
+
+package org.acme.proto.e;
+
+service TestE {
+ rpc PingSyncSync (PingRequestE) returns (PongResponseE) {}
+}
+
+message PingRequestE {
+ string ping_name = 1;
+}
+
+message PongResponseE {
+ string pong_name = 1;
+}
\ No newline at end of file
diff --git a/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/proto-e.proto b/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/proto-e.proto
new file mode 100644
index 0000000000..4be113a27d
--- /dev/null
+++ b/integration-tests-support/grpc/src/main/resources/org/acme/proto/e/proto-e.proto
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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";
+import "org/acme/proto/e/common.proto";
+import "google/protobuf/timestamp.proto";
diff --git a/integration-tests-support/pom.xml b/integration-tests-support/pom.xml
index 84ebb6b244..bd9292e7c7 100644
--- a/integration-tests-support/pom.xml
+++ b/integration-tests-support/pom.xml
@@ -45,6 +45,7 @@
<module>custom-type-converter</module>
<module>custom-main-listener</module>
<module>google</module>
+ <module>grpc</module>
<module>jdbc</module>
<module>kafka</module>
<module>mongodb</module>
diff --git a/integration-tests/grpc/pom.xml b/integration-tests/grpc/pom.xml
index c2e4dfee39..843227c5dd 100644
--- a/integration-tests/grpc/pom.xml
+++ b/integration-tests/grpc/pom.xml
@@ -30,10 +30,6 @@
<name>Camel Quarkus :: Integration Tests :: gRPC</name>
<description>Integration tests for Camel Quarkus gRPC extension</description>
- <properties>
- <cq.skip.protobuf>false</cq.skip.protobuf><!-- set to true via groovy below on unsupported platforms or if -Dquickly is available -->
- </properties>
-
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
@@ -59,6 +55,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-integration-tests-support-grpc</artifactId>
+ </dependency>
<!-- test dependencies -->
<dependency>
@@ -79,6 +79,7 @@
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
+ <scope>test</scope>
</dependency>
</dependencies>
@@ -128,116 +129,16 @@
</executions>
</plugin>
<plugin>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>detect-os</id>
- <phase>initialize</phase>
- <goals>
- <goal>detect</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.codehaus.gmavenplus</groupId>
- <artifactId>gmavenplus-plugin</artifactId>
- <executions>
- <execution>
- <!-- Skip code generation & tests on unsupported platforms -->
- <id>skip-plugins-on-unsupported-platform</id>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <scripts>
- <script>
- String platformUnsupported = project.properties['os.detected.classifier'].matches('^.*?(linux|windows|osx)-x86.*$') ? 'false' : 'true'
- project.properties['skipTests'] = platformUnsupported
- project.properties['cq.skip.protobuf'] = platformUnsupported || project.properties.containsKey('quickly')
- </script>
- </scripts>
- </configuration>
- </execution>
- <execution>
- <!--
- Hack around grpc-java protobuf generator tagging generated classes with javax.annotation.Generated.
-
- This avoids implementing the recommendation to add a dependency on org.apache.tomcat:annotations-api:
-
- https://github.com/grpc/grpc-java#download
-
- Hopefully this will eventually get fixed for the Jakarta 10+ world:
-
- https://github.com/grpc/grpc-java/issues/9179
- -->
- <id>strip-javax-generated-annotation</id>
- <phase>process-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <scripts>
- <script>
- def buildDir = project.build.directory
- new File("${buildDir}/generated-sources").eachFileRecurse { file ->
- if (file.name.endsWith('.java') && file.text.contains('javax.annotation.Generated')) {
- def modifiedContent = file.text.replace('javax', 'jakarta')
- file.write(modifiedContent)
- }
- }
- </script>
- </scripts>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- <phase>generate-sources</phase>
- <configuration>
- <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- <checkStaleness>true</checkStaleness>
- <skip>${cq.skip.protobuf}</skip>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <executions>
- <execution>
- <id>default-compile</id>
- <phase>compile</phase>
- <goals>
- <goal>compile</goal>
+ <goal>build</goal>
+ <goal>generate-code</goal>
+ <goal>generate-code-tests</goal>
</goals>
- <configuration>
- <skipMain>${cq.skip.protobuf}</skipMain>
- </configuration>
- </execution>
- <execution>
- <id>default-testCompile</id>
- <phase>test-compile</phase>
- <goals>
- <goal>testCompile</goal>
- </goals>
- <configuration>
- <skip>${cq.skip.protobuf}</skip>
- </configuration>
</execution>
</executions>
</plugin>
diff --git a/integration-tests/grpc/src/main/proto/pingpong.proto b/integration-tests/grpc/src/main/proto/pingpong.proto
index 459a3b33af..2e09f3bc1a 100644
--- a/integration-tests/grpc/src/main/proto/pingpong.proto
+++ b/integration-tests/grpc/src/main/proto/pingpong.proto
@@ -16,6 +16,9 @@
*/
syntax = "proto3";
+// Tests that arbitrary imports can work
+import "google/protobuf/timestamp.proto";
+
option java_multiple_files = true;
option java_package = "org.apache.camel.quarkus.component.grpc.it.model";
option java_outer_classname = "PingPongProto";
diff --git a/integration-tests/grpc/src/main/resources/application.properties b/integration-tests/grpc/src/main/resources/application.properties
index 7fb36b14c9..63661b9b4a 100644
--- a/integration-tests/grpc/src/main/resources/application.properties
+++ b/integration-tests/grpc/src/main/resources/application.properties
@@ -16,3 +16,11 @@
## ---------------------------------------------------------------------------
quarkus.native.resources.includes=certs/*.key,certs/*.pem,keys/*.json
+
+# Test codegen protobuf imports
+quarkus.camel.grpc.codegen.scan-for-imports=com.google.protobuf:protobuf-java,org.apache.camel.quarkus:camel-quarkus-integration-tests-support-grpc
+
+# Test codegen scanning for proto files in other dependencies
+quarkus.camel.grpc.codegen.scan-for-proto=org.apache.camel.quarkus:camel-quarkus-integration-tests-support-grpc
+quarkus.camel.grpc.codegen.scan-for-proto-includes."org.apache.camel.quarkus\:camel-quarkus-integration-tests-support-grpc"=org/acme/proto/a/*,org/acme/proto/b/proto-b.proto,org/acme/proto/c/**,org/acme/proto/d/**,org/acme/proto/e/**
+quarkus.camel.grpc.codegen.scan-for-proto-excludes."org.apache.camel.quarkus\:camel-quarkus-integration-tests-support-grpc"=org/acme/proto/d/*,org/acme/proto/d/sub/proto-d-2.proto,org/acme/proto/d/sub/package**
diff --git a/integration-tests/grpc/src/test/java/org/apache/camel/quarkus/component/grpc/it/GrpcTest.java b/integration-tests/grpc/src/test/java/org/apache/camel/quarkus/component/grpc/it/GrpcTest.java
index 2a891f32b6..fdae550b34 100644
--- a/integration-tests/grpc/src/test/java/org/apache/camel/quarkus/component/grpc/it/GrpcTest.java
+++ b/integration-tests/grpc/src/test/java/org/apache/camel/quarkus/component/grpc/it/GrpcTest.java
@@ -17,6 +17,7 @@
package org.apache.camel.quarkus.component.grpc.it;
import java.util.Iterator;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@@ -39,6 +40,7 @@ import org.apache.camel.quarkus.component.grpc.it.model.PingPongGrpc.PingPongBlo
import org.apache.camel.quarkus.component.grpc.it.model.PingPongGrpc.PingPongStub;
import org.apache.camel.quarkus.component.grpc.it.model.PingRequest;
import org.apache.camel.quarkus.component.grpc.it.model.PongResponse;
+import org.apache.camel.util.StringHelper;
import org.awaitility.Awaitility;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
@@ -62,6 +64,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
@QuarkusTest
@QuarkusTestResource(GrpcServerTestResource.class)
@@ -574,6 +577,25 @@ class GrpcTest {
}
}
+ @ParameterizedTest
+ @MethodSource("generatedProtoClassNames")
+ public void codeGenDependencyScan(String generatedClassPackage) {
+ // Additional proto files scanned from org.apache.camel.quarkus:camel-quarkus-integration-tests-support-grpc
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ String packageChar = StringHelper.after(generatedClassPackage, "org.acme.proto.").replaceAll(".sub|.dir", "");
+ String generatedClassName = generatedClassPackage + ".PingPongProto" + packageChar.toUpperCase();
+ try {
+ classLoader.loadClass(generatedClassName);
+ if (packageChar.equals("d")) {
+ fail("Expected to not be able to load generated class: " + generatedClassName);
+ }
+ } catch (ClassNotFoundException e) {
+ if (!packageChar.equals("d")) {
+ fail("Expected to be able to load generated class: " + generatedClassName);
+ }
+ }
+ }
+
static Stream<Arguments> producerMethodPorts() {
return Stream.of(
Arguments.of("pingSyncSync", "{{camel.grpc.test.async.server.port}}"),
@@ -584,6 +606,20 @@ class GrpcTest {
Arguments.of("pingAsyncAsync", "{{camel.grpc.test.sync.server.port}}"));
}
+ static List<String> generatedProtoClassNames() {
+ String packagePrefix = "org.acme.proto.";
+ return List.of(
+ packagePrefix + "a",
+ packagePrefix + "b",
+ packagePrefix + "c",
+ packagePrefix + "c.sub",
+ packagePrefix + "c.sub.dir",
+ packagePrefix + "d",
+ packagePrefix + "d.sub",
+ packagePrefix + "d.sub.dir",
+ packagePrefix + "e");
+ }
+
static final class PongResponseStreamObserver implements StreamObserver<PongResponse> {
private final CountDownLatch latch;
private final boolean simulateError;
diff --git a/pom.xml b/pom.xml
index 31f015663d..146fa3282c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -200,8 +200,6 @@
<maven-scm-plugin.version>2.0.0</maven-scm-plugin.version>
<maven-shade-plugin.version>3.5.0</maven-shade-plugin.version>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
- <os-maven-plugin.version>1.7.1</os-maven-plugin.version>
- <protobuf-maven-plugin.version>${protobuf-maven-plugin-version}</protobuf-maven-plugin.version>
<rpkgtests-maven-plugin.version>1.0.0</rpkgtests-maven-plugin.version>
<!-- Test container image properties -->
diff --git a/poms/bom-test/pom.xml b/poms/bom-test/pom.xml
index 077be210a4..df923ebe13 100644
--- a/poms/bom-test/pom.xml
+++ b/poms/bom-test/pom.xml
@@ -175,6 +175,11 @@
<artifactId>camel-quarkus-integration-tests-support-google</artifactId>
<version>${camel-quarkus.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-integration-tests-support-grpc</artifactId>
+ <version>${camel-quarkus.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-integration-test-support</artifactId>
diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml
index 4bab975644..58665b94e6 100644
--- a/poms/bom/pom.xml
+++ b/poms/bom/pom.xml
@@ -3837,6 +3837,11 @@
<artifactId>camel-quarkus-grpc-deployment</artifactId>
<version>${camel-quarkus.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-grpc-codegen</artifactId>
+ <version>${camel-quarkus.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-gson</artifactId>
diff --git a/poms/bom/src/main/generated/flattened-full-pom.xml b/poms/bom/src/main/generated/flattened-full-pom.xml
index fdece605a3..498a885125 100644
--- a/poms/bom/src/main/generated/flattened-full-pom.xml
+++ b/poms/bom/src/main/generated/flattened-full-pom.xml
@@ -3763,6 +3763,11 @@
<artifactId>camel-quarkus-grpc-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<version>3.0.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ <artifactId>camel-quarkus-grpc-codegen</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ <version>3.0.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<artifactId>camel-quarkus-gson</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
diff --git a/poms/bom/src/main/generated/flattened-reduced-pom.xml b/poms/bom/src/main/generated/flattened-reduced-pom.xml
index 726a1de96e..69509ddae3 100644
--- a/poms/bom/src/main/generated/flattened-reduced-pom.xml
+++ b/poms/bom/src/main/generated/flattened-reduced-pom.xml
@@ -3763,6 +3763,11 @@
<artifactId>camel-quarkus-grpc-deployment</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-grpc-codegen</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-gson</artifactId>
diff --git a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml
index fc09ab6bda..de31528b04 100644
--- a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml
+++ b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml
@@ -3763,6 +3763,11 @@
<artifactId>camel-quarkus-grpc-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<version>3.0.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ <artifactId>camel-quarkus-grpc-codegen</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ <version>3.0.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<artifactId>camel-quarkus-gson</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
diff --git a/poms/build-parent/pom.xml b/poms/build-parent/pom.xml
index ca0f8efd25..64ad613061 100644
--- a/poms/build-parent/pom.xml
+++ b/poms/build-parent/pom.xml
@@ -71,11 +71,6 @@
</execution>
</executions>
</plugin>
- <plugin>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>${os-maven-plugin.version}</version>
- </plugin>
<plugin>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-maven-plugin</artifactId>
@@ -100,11 +95,6 @@
<artifactId>jandex-maven-plugin</artifactId>
<version>${jandex-maven-plugin.version}</version>
</plugin>
- <plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>${protobuf-maven-plugin.version}</version>
- </plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>