Search code examples
javaprotocol-buffersguiceprotoc

Java : io.protostuff.compiler.parser.ParserException: Can not load proto


I am using guice parser to parse proto file. It is not able to resolve imports. Any suggestions experts in how to resolve imports public as well as imports within same project ?

Proto file

syntax = "proto3";
     package grpc;
     
     import "envoyproxy/protoc-gen-validate/validate/validate.proto";
     import "google/api/annotations.proto";
     import "google/protobuf/timestamp.proto";
     import "google/rpc/status.proto";
     import "google/protobuf/struct.proto";
     
     option go_package = "bitbucket.mycompany.com";
     option java_multiple_files = true;
     
     message SendRequest {
         string data = 1 ;
         bytes document = 2 ;
     }
     
     message SendResponse {
         string Status = 1;
         string Message = 2;
         repeated string DocumentList = 3;
     }
  

Dependencies in pom.xml for guice parser

<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-parser</artifactId>
    <version>3.1.38</version>
</dependency>
<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>5.1.0</version>
</dependency>

My Main application code

    Path currentDir = Paths.get("src\\main\\resources");
    System.out.println(currentDir.toAbsolutePath());

    final Injector injector = Guice.createInjector(new ParserModule());
    final Importer importer = injector.getInstance(Importer.class); 
    final ProtoContext protoContext = importer.importFile(new LocalFileReader(currentDir), "test.proto");
        
    final Proto proto = protoContext.getProto();
    final List<Message> messages = proto.getMessages();
    System.out.println(String.format("Messages: %s", messages));   

I am getting below exception when I run the application

Exception in thread "main" io.protostuff.compiler.parser.ParserException: Can not load proto: envoyproxy/protoc-gen-validate/validate/validate.proto not found
    at io.protostuff.compiler.parser.FileDescriptorLoaderImpl.load(FileDescriptorLoaderImpl.java:35)
    at io.protostuff.compiler.parser.ImporterImpl.importFile(ImporterImpl.java:34)

Solution

  • Introduction

    Let's consider the 3.1.38 version of the io.protostuff:protostuff-parser library as the current version.

    Solution

    It is necessary to use the FileReaderFactory and FileReader interfaces to get the «Include directories» support.

    The solution is inspired by: protostuff-compiler/ProtostuffCompiler.java at v3.1.38 · protostuff/protostuff-compiler.

    Example program

    Let's assume the /some/directory/imports directory is used for the imports.

    /some/directory/imports$ tree -d -L 2
    .
    ├── envoyproxy
    │   └── protoc-gen-validate
    └── google
        ├── api
        ├── protobuf
        └── rpc
    

    The program source code:

    import com.google.inject.Guice;
    import com.google.inject.Injector;
    import io.protostuff.compiler.ParserModule;
    import io.protostuff.compiler.model.Message;
    import io.protostuff.compiler.model.Proto;
    import io.protostuff.compiler.parser.FileReader;
    import io.protostuff.compiler.parser.FileReaderFactory;
    import io.protostuff.compiler.parser.Importer;
    import io.protostuff.compiler.parser.ProtoContext;
    import java.nio.file.Path;
    import java.util.List;
    
    public final class Program {
        public static void main(final String[] args) {
            final Injector injector = Guice.createInjector(new ParserModule());
    
            final FileReaderFactory fileReaderFactory = injector.getInstance(FileReaderFactory.class);
            final List<Path> includePaths = List.of(
                Path.of("/some/directory/imports")
            );
            final FileReader fileReader = fileReaderFactory.create(includePaths);
    
            final Importer importer = injector.getInstance(Importer.class);
            final ProtoContext protoContext = importer.importFile(
                fileReader,
                "/some/other/directory/test.proto"
            );
    
            final Proto proto = protoContext.getProto();
    
            final List<Message> messages = proto.getMessages();
            System.out.println(String.format("Messages: %s", messages));
        }
    }
    

    The program output:

    Messages: [Message{name=SendRequest, fullyQualifiedName=.grpc.SendRequest, fields=[Field{name=data, typeName=string, tag=1, options=DynamicMessage{fields={}}}, Field{name=document, typeName=bytes, tag=2, options=DynamicMessage{fields={}}}], options=DynamicMessage{fields={}}}, Message{name=SendResponse, fullyQualifiedName=.grpc.SendResponse, fields=[Field{name=Status, typeName=string, tag=1, options=DynamicMessage{fields={}}}, Field{name=Message, typeName=string, tag=2, options=DynamicMessage{fields={}}}, Field{name=DocumentList, modifier=repeated, typeName=string, tag=3, options=DynamicMessage{fields={}}}], options=DynamicMessage{fields={}}}]