Skip to content

Commit

Permalink
chore(transcoding): moved some generic methods to util classes
Browse files Browse the repository at this point in the history
  • Loading branch information
zZHorizonZz committed Apr 24, 2024
1 parent 82bde60 commit 76744d6
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 188 deletions.
2 changes: 1 addition & 1 deletion devtools/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<module>platform-properties</module>
<module>bom-descriptor-json</module>
<module>maven</module>
<!--module>gradle</module-->
<module>gradle</module>
<module>cli</module>
</modules>
</project>
6 changes: 1 addition & 5 deletions extensions/grpc/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,10 @@
<artifactId>protobuf-java-util</artifactId>
<version>3.24.3</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
</dependencies>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class GrpcCodeGen implements CodeGenProvider {
private static final String DESCRIPTOR_SET_OUTPUT_DIR = "quarkus.generate-code.grpc.descriptor-set.output-dir";
private static final String DESCRIPTOR_SET_FILENAME = "quarkus.generate-code.grpc.descriptor-set.name";

private static final String TRANSCODING_ENABLED = "quarkus.grpc.transcoding.enabled";

private static final String USE_ARG_FILE = "quarkus.generate-code.grpc.use-arg-file";

private Executables executables;
Expand Down Expand Up @@ -141,7 +143,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
}

if (!protoFiles.isEmpty()) {
initExecutables(workDir, context.applicationModel());
initExecutables(context, workDir, context.applicationModel());

Collection<String> protosToImport = gatherDirectoriesWithImports(workDir.resolve("protoc-dependencies"),
context);
Expand Down Expand Up @@ -241,7 +243,6 @@ private void postprocessing(CodeGenContext context, Path outDir) {
}

new GrpcPostProcessing(context, outDir).postprocess();

}

private Collection<Path> gatherProtosFromDependencies(Path workDir, Set<String> protoDirectories,
Expand Down Expand Up @@ -411,11 +412,12 @@ private String escapeWhitespace(String path) {
}
}

private void initExecutables(Path workDir, ApplicationModel model) throws CodeGenException {
private void initExecutables(CodeGenContext context, 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_GROUPID, PROTOC, classifier, EXE);
} else {
Expand All @@ -426,7 +428,7 @@ private void initExecutables(Path workDir, ApplicationModel model) throws CodeGe
Path protocGrpcPluginExe = prepareExecutable(workDir, model,
"io.grpc", "protoc-gen-grpc-java", classifier, "exe");

Path quarkusGrpcPluginExe = prepareQuarkusGrpcExecutable(model, workDir);
Path quarkusGrpcPluginExe = prepareQuarkusGrpcExecutable(context, model, workDir);

executables = new Executables(protocExe, protocGrpcPluginExe, quarkusGrpcPluginExe);
}
Expand Down Expand Up @@ -493,26 +495,28 @@ private String osClassifier() throws CodeGenException {
}
}

private static Path prepareQuarkusGrpcExecutable(ApplicationModel appModel, Path buildDir) throws CodeGenException {
private static Path prepareQuarkusGrpcExecutable(CodeGenContext context, ApplicationModel appModel, Path buildDir)
throws CodeGenException {
Path pluginPath = findArtifactPath(appModel, "io.quarkus", "quarkus-grpc-protoc-plugin", "shaded", "jar");
if (pluginPath == null) {
throw new CodeGenException("Failed to find Quarkus gRPC protoc plugin among dependencies");
}

if (OS.determineOS() != OS.WINDOWS) {
return writeScript(buildDir, pluginPath, "#!/bin/sh\n", ".sh");
return writeScript(context, buildDir, pluginPath, "#!/bin/sh\n", ".sh");
} else {
return writeScript(buildDir, pluginPath, "@echo off\r\n", ".cmd");
return writeScript(context, buildDir, pluginPath, "@echo off\r\n", ".cmd");
}
}

private static Path writeScript(Path buildDir, Path pluginPath, String shebang, String suffix) throws CodeGenException {
private static Path writeScript(CodeGenContext context, Path buildDir, Path pluginPath, String shebang, String suffix)
throws CodeGenException {
Path script;
try {
script = Files.createTempFile(buildDir, "quarkus-grpc", suffix);
try (BufferedWriter writer = Files.newBufferedWriter(script)) {
writer.write(shebang);
writePluginExeCmd(pluginPath, writer);
writePluginExeCmd(context, pluginPath, writer);
}
} catch (IOException e) {
throw new CodeGenException("Failed to create a wrapper script for quarkus-grpc plugin", e);
Expand All @@ -523,9 +527,13 @@ private static Path writeScript(Path buildDir, Path pluginPath, String shebang,
return script;
}

private static void writePluginExeCmd(Path pluginPath, BufferedWriter writer) throws IOException {
private static void writePluginExeCmd(CodeGenContext context, Path pluginPath, BufferedWriter writer) throws IOException {
Config properties = context.config();
boolean enableTranscoding = properties.getOptionalValue(TRANSCODING_ENABLED, Boolean.class).orElse(false);

writer.write("\"" + JavaBinFinder.findBin() + "\" -cp \"" +
pluginPath.toAbsolutePath() + "\" " + quarkusProtocPluginMain);
pluginPath.toAbsolutePath() + "\" " + quarkusProtocPluginMain
+ (enableTranscoding ? " --enableTranscoding" : ""));
writer.newLine();
}

Expand Down
1 change: 0 additions & 1 deletion extensions/grpc/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@
<artifactId>quarkus-grpc-protoc-plugin</artifactId>
<version>${project.version}</version>
<mainClass>io.quarkus.grpc.protoc.plugin.MutinyGrpcGenerator</mainClass>
<args>--skipTranscoding</args>
</protocPlugin>
</protocPlugins>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,13 @@ private MethodContext buildMethodContext(DescriptorProtos.MethodDescriptorProto
methodContext.httpPath = methodContext.methodName;
methodContext.httpMethod = "GET";

log.info("HTTP rule: " + methodProto.getOptions().hasExtension(AnnotationsProto.http) + " Skip transcoding: "
+ skipTranscoding);
if (!skipTranscoding && methodProto.getOptions().hasExtension(AnnotationsProto.http)) {
// Extract the HTTP rule from the method options (if present)
HttpRule httpRule = getHttpRule(methodProto);
if (httpRule == null) {
throw new IllegalArgumentException("HTTP rule is not defined for method " + methodProto.getName());
}

log.info("HTTP rule: " + httpRule);

// Determine HTTP method and path based on the HTTP rule
if (httpRule.hasGet()) {
methodContext.httpMethod = "GET";
Expand Down Expand Up @@ -473,22 +469,19 @@ public String methodHeader() {
public static void main(String[] args) {
Set<String> argSet = new HashSet<>(Arrays.asList(args));
if (argSet.contains("help") || argSet.contains("--help") || argSet.contains("-h")) {
System.out.println("Usage: MutinyGrpcGenerator [skipTranscoding]");
System.out.println("--skipTranscoding: Skip the generation of transcoding classes");
System.out.println("Usage: MutinyGrpcGenerator [enableTranscoding]");
System.out.println("--enableTranscoding: Enable transcoding support");
return;
}

boolean skipTranscoding = false;
if (args.length > 0) {
skipTranscoding = argSet.contains("--skipTranscoding");
log.info("Skipping transcoding...");
}
boolean skipTranscoding = !argSet.contains("--enableTranscoding");
log.info("Transcoding will be " + (skipTranscoding ? "skipped" : "enabled"));

String[] finalArgs = skipTranscoding ? Arrays.stream(args).skip(1).toArray(String[]::new) : args;
String[] finalArgs = skipTranscoding ? args : Arrays.copyOfRange(args, 1, args.length);
if (finalArgs.length == 0) {
ProtocPlugin.generate(List.of(new MutinyGrpcGenerator(skipTranscoding)), List.of(AnnotationsProto.http));
} else {
ProtocPlugin.debug(List.of(new MutinyGrpcGenerator()), List.of(AnnotationsProto.http), args[0]);
ProtocPlugin.debug(List.of(new MutinyGrpcGenerator()), List.of(AnnotationsProto.http), finalArgs[0]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class {{serviceName}}Marshalling implements GrpcTranscoding {
return "{{packageName}}.{{serviceName}}";
}

@SuppressWarnings("unchecked")
@Override
public <Req extends Message, Resp extends Message> GrpcTranscodingDescriptor<Req, Resp> findTranscodingDescriptor(String method) {
switch (method) {
Expand Down
10 changes: 1 addition & 9 deletions extensions/grpc/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@
<groupId>com.google.api.grpc</groupId>
<artifactId>proto-google-common-protos</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
Expand Down Expand Up @@ -108,7 +100,7 @@
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-vertx-context</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ private void buildGrpcServer(Vertx vertx, GrpcServerConfiguration configuration,

LOGGER.info("Starting new Quarkus gRPC server (using Vert.x transport)...");
Route route = routerSupplier.getValue().route().handler(ctx -> {
LOGGER.info("Request received => " + ctx.request().path());

if (!isGrpc(ctx)) {
ctx.next();
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.grpc.runtime.config;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

/**
* Configuration root for gRPC Transcoding feature in Quarkus. gRPC Transcoding allows you to create
* RESTful JSON APIs that are backed by existing gRPC services.
*/
@ConfigRoot(name = "grpc.transcoding", phase = ConfigPhase.BUILD_TIME)
public class GrpcTranscodingConfig {

/**
* Flag to enable or disable the gRPC Transcoding feature.
* The default value is `false` (disabled).
*/
@ConfigItem(defaultValue = "false")
public boolean enabled;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.quarkus.grpc.transcoding;

import java.util.HashMap;
import java.util.Map;

/**
* The `GrpcTranscodingHttpUtils` class provides utility functions for path handling
* and parameter extraction during the gRPC message transcoding process. Its key
* functions include:
* <p>
* Checking if a request path matches a given gRPC path template.
* Extracting path parameters from both gRPC path templates and concrete HTTP paths.
*/
public class GrpcTranscodingHttpUtils {

/**
* Determines if a given HTTP request path conforms to a specified gRPC path template.
*
* @param requestPath The actual HTTP request path to be checked.
* @param pathTemplate The gRPC path template defining the expected structure.
* @return `true` if the paths match, `false` otherwise.
*/
public static boolean isPathMatch(String requestPath, String pathTemplate) {
int pathIndex = 0;
int templateIndex = 0;

while (pathIndex < requestPath.length() && templateIndex < pathTemplate.length()) {
int pathEnd = requestPath.indexOf('/', pathIndex);
int templateEnd = pathTemplate.indexOf('/', templateIndex);

// Extract the current segment from both paths
String requestPart = pathEnd == -1 ? requestPath.substring(pathIndex) : requestPath.substring(pathIndex, pathEnd);
String templatePart = templateEnd == -1 ? pathTemplate.substring(templateIndex)
: pathTemplate.substring(templateIndex, templateEnd);

// Check if the template part is a variable segment
if (templatePart.startsWith("{") && templatePart.endsWith("}")) {
if (requestPart.isEmpty()) {
return false;
}
// Skip to the end of the next segment
pathIndex = pathEnd != -1 ? pathEnd + 1 : requestPath.length();
templateIndex = templateEnd != -1 ? templateEnd + 1 : pathTemplate.length();
} else {
if (!requestPart.equals(templatePart)) {
return false;
}

// Skip to the end of the next segment
pathIndex = pathEnd != -1 ? pathEnd + 1 : requestPath.length();
templateIndex = templateEnd != -1 ? templateEnd + 1 : pathTemplate.length();
}
}

// Ensure both paths have been fully consumed
return pathIndex == requestPath.length() && templateIndex == pathTemplate.length();
}

/**
* Extracts path parameters from a gRPC path template and an associated HTTP path.
*
* @param pathTemplate The gRPC path template defining the parameter structure.
* @param httpPath The actual HTTP path from which to extract the parameter values.
* @return A `Map` containing the extracted parameter names and their corresponding values.
*/
public static Map<String, String> extractPathParams(String pathTemplate, String httpPath) {
Map<String, String> extractedParams = new HashMap<>();

String[] pathParts = httpPath.split("/");
String[] templateParts = pathTemplate.split("/");

for (int i = 0; i < pathParts.length; i++) {
String pathPart = pathParts[i];
String templatePart = templateParts[i];

if (templatePart.startsWith("{") && templatePart.endsWith("}")) {
String paramName = templatePart.substring(1, templatePart.length() - 1);
extractedParams.put(paramName, pathPart);
}
}

return extractedParams;
}
}
Loading

0 comments on commit 76744d6

Please sign in to comment.