Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native gRPC application: missing method "valueOf" #24510

Closed
ron-mcleod opened this issue Mar 23, 2022 · 6 comments · Fixed by #24535
Closed

Native gRPC application: missing method "valueOf" #24510

ron-mcleod opened this issue Mar 23, 2022 · 6 comments · Fixed by #24535
Labels
area/grpc gRPC kind/bug Something isn't working
Milestone

Comments

@ron-mcleod
Copy link

ron-mcleod commented Mar 23, 2022

Describe the bug

I am seeing an issue when I have a gRPC message with an inner-enum. If I run the application in dev mode or from a JAR, all is fine, but when I build and run as a native app, an exception is thrown complaining that the valueOffor the enum is missing. I have checked the generated code and the method is actually present. I did notice that the method was annotated with @deprecated (not sure if this is relevant).

This occurs with both the gRPC server application and gRPC client application.

Expected behavior

Should run without any complaints.

Actual behavior

2022-03-23 12:58:35,141 INFO  [io.quarkus] (main) grpc-test 1.0.0 native (powered by Quarkus 2.7.5.Final) started in 0.038s.
2022-03-23 12:58:35,142 INFO  [io.quarkus] (main) Profile prod activated.
2022-03-23 12:58:35,142 INFO  [io.quarkus] (main) Installed features: [cdi, grpc-client, smallrye-context-propagation, vertx]
Calling remote service
2022-03-23 12:58:35,145 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): java.lang.NoSuchMethodException: net.example.grpctest.LanguageSpec$Language.valueOf(com.google.protobuf.Descriptors$EnumValueDescriptor)
        at java.lang.Class.getMethod(DynamicHub.java:1139)
        at com.google.protobuf.GeneratedMessageV3.getMethodOrDie(GeneratedMessageV3.java:1844)
        at com.google.protobuf.GeneratedMessageV3.access$1000(GeneratedMessageV3.java:79)
        at com.google.protobuf.GeneratedMessageV3$FieldAccessorTable$SingularEnumFieldAccessor.<init>(GeneratedMessageV3.java:2747)
        at com.google.protobuf.GeneratedMessageV3$FieldAccessorTable.ensureFieldAccessorsInitialized(GeneratedMessageV3.java:1973)
        at net.example.grpctest.LanguageSpec.internalGetFieldAccessorTable(LanguageSpec.java:43)
        at com.google.protobuf.GeneratedMessageV3.getDescriptorForType(GeneratedMessageV3.java:133)
        at com.google.protobuf.GeneratedMessageV3.isInitialized(GeneratedMessageV3.java:207)
        at net.example.grpctest.LanguageSpec$Builder.build(LanguageSpec.java:330)
        at net.example.RemoteService.specifyLanguage(RemoteService.java:18)
        at net.example.RemoteService_ClientProxy.specifyLanguage(Unknown Source)
        at net.example.Main$MyApp.run(Main.java:25)
        at net.example.Main_MyApp_ClientProxy.run(Unknown Source)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:124)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:67)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:41)
        at net.example.Main.main(Main.java:16)

2022-03-23 12:58:35,145 INFO  [io.qua.grp.run.sup.Channels] (main) Shutting down gRPC channel ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=dns:///127.0.0.1:8015}}
2022-03-23 12:58:35,166 INFO  [io.quarkus] (main) grpc-test stopped in 0.021s

How to Reproduce?

quarkus-grpc-issue.tar.gz

  1. Unpack gzipped tarball Maven project: tar -xzf quarkus-grpc-issue.tar.gz
  2. Build native application: mvn package -Pnative
  3. Run application: ./target/grpc-test-1.0.0-runner

Output of uname -a or ver

Linux rocky.example.net 4.18.0-305.10.2.el8_4.x86_64 #1 SMP Tue Jul 20 20:34:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

openjdk version "11.0.13" 2021-10-19 OpenJDK Runtime Environment Temurin-11.0.13+8 (build 11.0.13+8) OpenJDK 64-Bit Server VM Temurin-11.0.13+8 (build 11.0.13+8, mixed mode)

GraalVM version (if different from Java)

quay.io/quarkus/ubi-quarkus-native-image:21.3-java11
GraalVM 21.3.1 Java 11 CE (Java Version 11.0.14+9-jvmci-21.3-b09)

Quarkus version or git rev

Quarkus 2.7.5.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /opt/maven
Java version: 11.0.13, vendor: Eclipse Adoptium, runtime: /opt/jdk-11.0.13+8
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.18.0-305.10.2.el8_4.x86_64", arch: "amd64", family: "unix"

Additional information

No response

@ron-mcleod ron-mcleod added the kind/bug Something isn't working label Mar 23, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Mar 23, 2022

/cc @cescoffier, @michalszynkiewicz

@quarkus-bot quarkus-bot bot added the area/grpc gRPC label Mar 23, 2022
@ron-mcleod
Copy link
Author

Additional information: I found that the problem did not occur if I annotated the inner enum class with @RegisterForReflection (comments removed):

public final class LanguageSpec 
    extends com.google.protobuf.GeneratedMessageV3
    implements LanguageSpecOrBuilder {
  ...
  
  @RegisterForReflection // <=== added this ===
  public enum Language
      implements com.google.protobuf.ProtocolMessageEnum {
    ENGLISH(0),
    FRENCH(1),
    SPANISH(2),
    UNRECOGNIZED(-1),
    ;

    public static final int ENGLISH_VALUE = 0;
    public static final int FRENCH_VALUE = 1;
    public static final int SPANISH_VALUE = 2;

    public final int getNumber() {
      if (this == UNRECOGNIZED) {
        throw new java.lang.IllegalArgumentException(
            "Can't get the number of an unknown enum value.");
      }
      return value;
    }

    @java.lang.Deprecated
    public static Language valueOf(int value) {
      return forNumber(value);
    }

    public static Language forNumber(int value) {
      switch (value) {
        case 0: return ENGLISH;
        case 1: return FRENCH;
        case 2: return SPANISH;
        default: return null;
      }
    }

  ...

@ron-mcleod
Copy link
Author

Since the gRPC code is generated and should not be modified, I tried registering the problematic inner class using another class, it is did successfully work-around the missing method problem:

package net.example;

import io.quarkus.runtime.annotations.RegisterForReflection;
import net.example.grpctest.LanguageSpec;

@RegisterForReflection(targets = { LanguageSpec.Language.class })
public class GrpcInnerClassWorkAround {
}

@cescoffier
Copy link
Member

The issue is that we do not register the enum class. It should be done automatically.

cescoffier added a commit to cescoffier/quarkus that referenced this issue Mar 24, 2022
Protobuf enums generate enum classes.
Fortunately, we can locate them with Jandex using the ProtocolMessageEnum interface, as the generated enums implement this interface.

Fix quarkusio#24510.
@quarkus-bot quarkus-bot bot added this to the 2.9 - main milestone Mar 24, 2022
@gsmet gsmet modified the milestones: 2.9 - main, 2.8.0.Final Mar 28, 2022
gsmet pushed a commit to gsmet/quarkus that referenced this issue Mar 28, 2022
Protobuf enums generate enum classes.
Fortunately, we can locate them with Jandex using the ProtocolMessageEnum interface, as the generated enums implement this interface.

Fix quarkusio#24510.

(cherry picked from commit 612348e)
@gsmet gsmet modified the milestones: 2.8.0.Final, 2.7.6.Final May 5, 2022
gsmet pushed a commit to gsmet/quarkus that referenced this issue May 5, 2022
Protobuf enums generate enum classes.
Fortunately, we can locate them with Jandex using the ProtocolMessageEnum interface, as the generated enums implement this interface.

Fix quarkusio#24510.

(cherry picked from commit 612348e)
gsmet pushed a commit to gsmet/quarkus that referenced this issue May 5, 2022
Protobuf enums generate enum classes.
Fortunately, we can locate them with Jandex using the ProtocolMessageEnum interface, as the generated enums implement this interface.

Fix quarkusio#24510.

(cherry picked from commit 612348e)
@AndreyChukhlebov
Copy link

Reproduced on dependencies in owned generated enum protobuf-maven-plugin is it possible somehow to solve the problem without reflection-config.json

@xco-sk
Copy link

xco-sk commented Aug 24, 2023

Hello,

we stumbled upon the same issue when we were logging out the part of proto generated object, so when the toString() method is called:

Caused by: java.lang.IllegalStateException: Generated message class "protos.v1.MainMarketType" missing method "valueOf".  
    at com.google.protobuf.GeneratedMessageV3.getMethodOrDie(GeneratedMessageV3.java:1998)  
    at com.google.protobuf.GeneratedMessageV3.access$1000(GeneratedMessageV3.java:79)  
    at com.google.protobuf.GeneratedMessageV3$FieldAccessorTable$SingularEnumFieldAccessor.<init>(GeneratedMessageV3.java:2897)  
    at com.google.protobuf.GeneratedMessageV3$FieldAccessorTable.ensureFieldAccessorsInitialized(GeneratedMessageV3.java:2129)  
    at protos.v1.Market.internalGetFieldAccessorTable(Market.java:48)  
    at com.google.protobuf.GeneratedMessageV3.getDescriptorForType(GeneratedMessageV3.java:139)  
    at com.google.protobuf.TextFormat$Printer.print(TextFormat.java:363)  
    at com.google.protobuf.TextFormat$Printer.printFieldValue(TextFormat.java:606)  
    at com.google.protobuf.TextFormat$Printer.printSingleField(TextFormat.java:752)  
    at com.google.protobuf.TextFormat$Printer.printField(TextFormat.java:457)  
    at com.google.protobuf.TextFormat$Printer.printMessage(TextFormat.java:714)  
    at com.google.protobuf.TextFormat$Printer.print(TextFormat.java:367)  
    at com.google.protobuf.TextFormat$Printer.printFieldValue(TextFormat.java:606)  
    at com.google.protobuf.TextFormat$Printer.printSingleField(TextFormat.java:752)  
    at com.google.protobuf.TextFormat$Printer.printField(TextFormat.java:457)  
    at com.google.protobuf.TextFormat$Printer.printMessage(TextFormat.java:714)  
    at com.google.protobuf.TextFormat$Printer.print(TextFormat.java:367)  
    at com.google.protobuf.TextFormat$Printer.print(TextFormat.java:353)  
    at com.google.protobuf.TextFormat$Printer.printToString(TextFormat.java:615)  
    at com.google.protobuf.AbstractMessage.toString(AbstractMessage.java:110)  
    at [email protected]/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453)  
    at messagehandler.MessageHandlerLogFactory.createConversionExceptionMessage(MessageHandlerLogFactory.java:11)

The protos were generated with protobuf 3.21.12.
We use Quarkus 3.2.2.Final, built into a native image with GraalVM 22.3.1 and Java 17.
The native executable is then packaged into ubi9-minimal:9.2.

The protos.v1.MainMarketType is a proto Enum and is located in the library defined in the build.gradle file. It's also defined in application properties in quarkus.index-dependency so it's visible for Quarkus CDI. Everything seems to work fine, except the toString() on a proto-generated object that contains the protos.v1.MainMarketType-typed field.

The valueOf method is present in the generated class, even though annotated with @Deprecated annotation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/grpc gRPC kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants