Skip to content

Add a way to get the service descriptor from the stub/implbase #639

@friscoMad

Description

@friscoMad

Right now, it is challenging to reach the service descriptor from the stub in a generic way, and the stub is the main class that is used in the client code.

Our use case is to read some custom options set at the service level.

It is possible to jump from the stub to the service kotlin class using reflection and leveraging stub::class.java.enclosingClass or jumping to the java class by looking for the StubFor annotation. Still, both are implementation details that could change in the future.
From there, getting the descriptor also requires reflection, as none of the Kotlin or Java classes implement an interface.

Maybe I am missing some helper functions, but this is the hacky code I needed to read an option from the service:

    private fun <S : AbstractCoroutineStub<*>> getHost(stub: KClass<S>): String {
        val serviceClass = (stub.annotations.firstOrNull { it.annotationClass == StubFor::class } as StubFor?)?.value
        checkNotNull(serviceClass) { "service class not found from the stub" }
        val serviceDescriptorMethod = serviceClass.members.firstOrNull {
            it.name == "getServiceDescriptor" &&
                    it.parameters.isEmpty() &&
                    it.returnType.classifier == ServiceDescriptor::class
        }
        checkNotNull(serviceDescriptorMethod) { "service descriptor not found from the stub" }
        val schemaDescriptor = (serviceDescriptorMethod.call() as ServiceDescriptor).schemaDescriptor
        check(schemaDescriptor is ProtoServiceDescriptorSupplier) { "service descriptor is not a ProtoServiceDescriptor" }
        val serviceOptions = schemaDescriptor.serviceDescriptor.options
        val option = serviceOptions.getExtension(OptionClass.option)

From the implbase we don't even have an annotation to leverage, so we are limited to use the enclosing path.

In Java, this is easier as both impl and stub are built from the service class; still, it does not implement any interface, so it is difficult to access the get descriptor method without reflection, but you don't need additional hoops.

My suggestion would be to add accessors from the stub/impl to get the ServiceGrpcKt class and make all ServiceGrpcKt classes implement an interface like this:

interface ServiceDescriptorInterface {
  fun getDescriptor(): ServiceDescriptor
}

With that interface, you can access the name, the methods, and the service itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions