From ae4f01d10c607c469fd96983af148a1c488103d7 Mon Sep 17 00:00:00 2001 From: Andrzej Ludwikowski Date: Wed, 31 Jan 2024 16:31:34 +0100 Subject: [PATCH] switching DescriptorSet implementation --- .../io/kalix/codegen/DescriptorSet2.scala | 104 ++++++++++++++++++ .../main/scala/io/kalix/codegen/js/Cli.scala | 6 +- 2 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 codegen/core/src/main/scala/io/kalix/codegen/DescriptorSet2.scala diff --git a/codegen/core/src/main/scala/io/kalix/codegen/DescriptorSet2.scala b/codegen/core/src/main/scala/io/kalix/codegen/DescriptorSet2.scala new file mode 100644 index 00000000..02c4be85 --- /dev/null +++ b/codegen/core/src/main/scala/io/kalix/codegen/DescriptorSet2.scala @@ -0,0 +1,104 @@ +/* + * Copyright (c) Lightbend Inc. 2021 + * + */ + +package io.kalix.codegen + +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.util.logging.Level +import java.util.logging.Logger + +import scala.jdk.CollectionConverters._ +import scala.util.Failure +import scala.util.Success +import scala.util.Using + +import com.google.protobuf.ExtensionRegistry +import com.google.protobuf.DescriptorProtos +import com.google.protobuf.Descriptors + +/** + * Provides conveniences for reading and parsing Protobuf descriptor sets + */ +object DescriptorSet2 { + + /** + * The descriptor file cannot be opened + */ + final case class CannotOpen(e: Throwable) + + /** + * Various error conditions during a read + */ + sealed abstract class ReadFailure + case class CannotRead(e: IOException) extends ReadFailure + case class CannotValidate(e: Descriptors.DescriptorValidationException) extends ReadFailure + + /** + * Read Protobuf FileDescriptor objects from given a file hosting a DescriptorSet + * @param file + * the file to read + * @return + * a collection of FileDescriptor objects or an error condition + */ + def fileDescriptors( + file: java.io.File): Either[CannotOpen, Either[ReadFailure, Iterable[Descriptors.FileDescriptor]]] = + Using[FileInputStream, Either[CannotOpen, Either[ReadFailure, Iterable[Descriptors.FileDescriptor]]]]( + new FileInputStream(file)) { fis => descriptors(fis) } match { + case Success(result) => result + case Failure(e: FileNotFoundException) => Left(CannotOpen(e)) + case Failure(e) => throw e + } + + @SuppressWarnings(Array("org.wartremover.warts.Throw")) + def descriptors(is: InputStream): Either[CannotOpen, Either[ReadFailure, Iterable[Descriptors.FileDescriptor]]] = { + val registry = ExtensionRegistry.newInstance() + registry.add(kalix.Annotations.codegen) + registry.add(kalix.Annotations.service) + registry.add(kalix.Annotations.file) + registry.add(kalix.Annotations.method) + + Right(try { + val descriptorProtos = + DescriptorProtos.FileDescriptorSet.parseFrom(is, registry).getFileList.asScala + + val empty: Either[ReadFailure, Iterable[Descriptors.FileDescriptor]] = + Right(Array[Descriptors.FileDescriptor]()) + descriptorProtos.foldLeft(empty)((acc, file) => accumulatedBuildFrom(acc, file)) + } catch { + case e: IOException => + Left(CannotRead(e)) + }) + } + + private val descriptorslogger = Logger.getLogger(classOf[Descriptors].getName) + descriptorslogger.setLevel(Level.OFF); // Silence protobuf + + /** + * This method accumulates `FileDescriptor`s to provide all the necessary dependencies for each call to + * FileDescriptor.buildFrom. Otherwise placeholders (mocked references) get created instead and these can't function + * as proper dependencies. Chiefly as imports. + * + * see allowUnknownDependencies per + * https://github.com/protocolbuffers/protobuf/blob/ae26a81918fa9e16f64ac27b5a2fb2b110b7aa1b/java/core/src/main/java/com/google/protobuf/Descriptors.java#L286 + */ + private def accumulatedBuildFrom( + reads: Either[ReadFailure, Iterable[Descriptors.FileDescriptor]], + file: DescriptorProtos.FileDescriptorProto): Either[ReadFailure, Iterable[Descriptors.FileDescriptor]] = { + reads match { + case Left(_) => reads + case Right(fileDescriptors) => + try { + Right(fileDescriptors ++ List(Descriptors.FileDescriptor.buildFrom(file, fileDescriptors.toArray, true))) + } catch { + case e: Descriptors.DescriptorValidationException => + Left(CannotValidate(e)) + } + } + } + +} diff --git a/codegen/js-gen-cli/src/main/scala/io/kalix/codegen/js/Cli.scala b/codegen/js-gen-cli/src/main/scala/io/kalix/codegen/js/Cli.scala index 7ece039d..7ff4e498 100644 --- a/codegen/js-gen-cli/src/main/scala/io/kalix/codegen/js/Cli.scala +++ b/codegen/js-gen-cli/src/main/scala/io/kalix/codegen/js/Cli.scala @@ -82,10 +82,10 @@ object Cli { config.descriptorSetOutputDirectory.resolve(config.descriptorSetFileName).toFile if (protobufDescriptor.exists) { println("Inspecting proto file descriptor for Kalix code generation...") - val _ = DescriptorSet.fileDescriptors(protobufDescriptor) match { + val _ = DescriptorSet2.fileDescriptors(protobufDescriptor) match { case Right(fileDescriptors) => val model = - ModelBuilder.introspectProtobufClasses(fileDescriptors.map { + ModelBuilder.introspectProtobufClasses(fileDescriptors match { case Right(fileDescriptor) => fileDescriptor case Left(e) => System.err.println("There was a problem building the file descriptor from its protobuf:") @@ -109,7 +109,7 @@ object Cli { println("Generated: " + absBaseDir.relativize(p.toAbsolutePath).toString) } - case Left(DescriptorSet.CannotOpen(e)) => + case Left(DescriptorSet2.CannotOpen(e)) => System.err.println("There was a problem opening the protobuf descriptor file:") System.err.println(e.toString) sys.exit(1)