micronaut-grpc icon indicating copy to clipboard operation
micronaut-grpc copied to clipboard

Although I can generate the stubs with io.grpc:protoc-gen-grpc-kotlin I can't implement it

Open jimisdrpc opened this issue 4 years ago • 17 comments

Issue: I can't implement an object autogenerated from protobuf.

How reproduce: generated a project from micronaut initializer with: gRPC Application type, Java 11, Kotlin. Add io.grpc:protoc-gen-grpc-kotlin on build.gradle, build it, add a controller and try implement GrpcdemoServiceGrpcKt.

image

build.gradle

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.4.10"
    id "org.jetbrains.kotlin.kapt" version "1.4.10"
    id "org.jetbrains.kotlin.plugin.allopen" version "1.4.10"
    id "com.github.johnrengelman.shadow" version "6.1.0"
    id "io.micronaut.application" version '1.0.5'
    id "com.google.protobuf" version "0.8.13"
}

version "0.1"
group "com.mybank"

repositories {
    mavenCentral()
    jcenter()
}

micronaut {
    testRuntime "junit5"
    processing {
        incremental true
        annotations "com.mybank.*"
    }
}

dependencies {
    implementation("io.micronaut:micronaut-validation")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("javax.annotation:javax.annotation-api")
    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
    testImplementation("io.micronaut:micronaut-http-client")

    implementation("io.grpc:grpc-kotlin-stub:${grpcKotlinVersion}")
}

mainClassName = "com.mybank.ApplicationKt"
java {
    sourceCompatibility = JavaVersion.toVersion('11')
}

compileKotlin {
    kotlinOptions {
        jvmTarget = '11'
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = '11'
    }
}



sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.13.0" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.32.1" }
        grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:${grpcKotlinVersion}" }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
            grpckt {}
        }
    }
}

gradle.properties

micronautVersion=2.1.3
kotlinVersion=1.4.10
grpcKotlinVersion=0.1.2

auto generated stubs while gradle build

package com.mybank

import com.mybank.GrpcdemoServiceGrpc.getServiceDescriptor
import io.grpc.CallOptions
import io.grpc.CallOptions.DEFAULT
import io.grpc.Channel
import io.grpc.Metadata
import io.grpc.MethodDescriptor
import io.grpc.ServerServiceDefinition
import io.grpc.ServerServiceDefinition.builder
import io.grpc.ServiceDescriptor
import io.grpc.Status.UNIMPLEMENTED
import io.grpc.StatusException
import io.grpc.kotlin.AbstractCoroutineServerImpl
import io.grpc.kotlin.AbstractCoroutineStub
import io.grpc.kotlin.ClientCalls.unaryRpc
import io.grpc.kotlin.ServerCalls.unaryServerMethodDefinition
import io.grpc.kotlin.StubFor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic

/**
 * Holder for Kotlin coroutine-based client and server APIs for com.mybank.GrpcdemoService.
 */
object GrpcdemoServiceGrpcKt {
  @JvmStatic
  val serviceDescriptor: ServiceDescriptor
    get() = GrpcdemoServiceGrpc.getServiceDescriptor()

  val sendMethod: MethodDescriptor<GrpcdemoRequest, GrpcdemoReply>
    @JvmStatic
    get() = GrpcdemoServiceGrpc.getsendMethod()

  /**
   * A stub for issuing RPCs to a(n) com.mybank.GrpcdemoService service as suspending coroutines.
   */
  @StubFor(GrpcdemoServiceGrpc::class)
  class GrpcdemoServiceCoroutineStub @JvmOverloads constructor(
    channel: Channel,
    callOptions: CallOptions = DEFAULT
  ) : AbstractCoroutineStub<GrpcdemoServiceCoroutineStub>(channel, callOptions) {
    override fun build(channel: Channel, callOptions: CallOptions): GrpcdemoServiceCoroutineStub =
        GrpcdemoServiceCoroutineStub(channel, callOptions)

    /**
     * Executes this RPC and returns the response message, suspending until the RPC completes
     * with [`Status.OK`][io.grpc.Status].  If the RPC completes with another status, a
     * corresponding
     * [StatusException] is thrown.  If this coroutine is cancelled, the RPC is also cancelled
     * with the corresponding exception as a cause.
     *
     * @param request The request message to send to the server.
     *
     * @return The single response from the server.
     */
    suspend fun send(request: GrpcdemoRequest): GrpcdemoReply = unaryRpc(
      channel,
      GrpcdemoServiceGrpc.getSendMethod(),
      request,
      callOptions,
      Metadata()
    )}

  /**
   * Skeletal implementation of the com.mybank.GrpcdemoService service based on Kotlin coroutines.
   */
  abstract class GrpcdemoServiceCoroutineImplBase(
    coroutineContext: CoroutineContext = EmptyCoroutineContext
  ) : AbstractCoroutineServerImpl(coroutineContext) {
    /**
     * Returns the response to an RPC for com.mybank.GrpcdemoService.send.
     *
     * If this method fails with a [StatusException], the RPC will fail with the corresponding
     * [io.grpc.Status].  If this method fails with a [java.util.concurrent.CancellationException],
     * the RPC will fail
     * with status `Status.CANCELLED`.  If this method fails for any other reason, the RPC will
     * fail with `Status.UNKNOWN` with the exception as a cause.
     *
     * @param request The request from the client.
     */
    open suspend fun send(request: GrpcdemoRequest): GrpcdemoReply = throw
        StatusException(UNIMPLEMENTED.withDescription("Method com.mybank.GrpcdemoService.send is unimplemented"))

    final override fun bindService(): ServerServiceDefinition = builder(getServiceDescriptor())
      .addMethod(unaryServerMethodDefinition(
      context = this.context,
      descriptor = GrpcdemoServiceGrpc.getSendMethod(),
      implementation = ::send
    )).build()
  }
}

All the rest are exactly the same from micronaut.launch

Possible solution: there is an example I downloaded and staret it successsfuly and called it from BloomRPC. It is from oficial examples. Looking at it I see a much more complex gradle.

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.3.72"
    id "org.jetbrains.kotlin.kapt" version "1.3.72"
    id "org.jetbrains.kotlin.plugin.allopen" version "1.3.72"
    id "application"
    id 'com.google.protobuf' version '0.8.13'
}

version "0.2"
group "helloworld"

repositories {
    mavenLocal()
    jcenter()
}

configurations {
    // for dependencies that are needed for development only
    developmentOnly
}

dependencies {
    kapt(enforcedPlatform("io.micronaut:micronaut-bom:$micronautVersion"))
    kapt("io.micronaut:micronaut-inject-java")
    kapt("io.micronaut:micronaut-validation")

    implementation(enforcedPlatform("io.micronaut:micronaut-bom:$micronautVersion"))
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")
    implementation("io.micronaut:micronaut-runtime")
//    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-server-runtime:$micronautGrpcVersion")
    implementation("io.micronaut.grpc:micronaut-grpc-client-runtime:$micronautGrpcVersion")
    implementation("io.grpc:grpc-kotlin-stub:${grpcKotlinVersion}")

    runtimeOnly("ch.qos.logback:logback-classic:1.2.3")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")

    kaptTest("io.micronaut:micronaut-inject-java")

    testImplementation enforcedPlatform("io.micronaut:micronaut-bom:$micronautVersion")
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.0")
    testImplementation("io.micronaut.test:micronaut-test-junit5")
    testImplementation("org.mockito:mockito-junit-jupiter:2.22.0")

    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.3.0")
    testRuntime("org.jetbrains.spek:spek-junit-platform-engine:1.1.5")
}

test.classpath += configurations.developmentOnly

mainClassName = "helloworld.Application"

test {
    useJUnitPlatform()
}

allOpen {
	annotation("io.micronaut.aop.Around")
}

compileKotlin {
	kotlinOptions {
	    jvmTarget = '1.8' 
	    //Will retain parameter names for Java reflection
	    javaParameters = true 
	}
}
//compileKotlin.dependsOn(generateProto)

compileTestKotlin {
	kotlinOptions {
	    jvmTarget = '1.8' 
	    javaParameters = true 
	}
}

tasks.withType(JavaExec) {
    classpath += configurations.developmentOnly
    jvmArgs('-XX:TieredStopAtLevel=1', '-Dcom.sun.management.jmxremote')
}

sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/grpckt'
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
        grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:${grpcKotlinVersion}" }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
            grpckt {}
        }
    }
}

Question: what I am missing in order to implement the autogenerated stubs? Do I need more gradle dependencies beyond io.grpc:protoc-gen-grpc-kotlin? Am I in right direction? If not, what should I do in order to implement the send rpc method from demo project downloaded from Micronaut.launch?

PS.: when I tried the most recent version from io.grpc:protoc-gen-grpc-kotlin gradle complains so I just use 0.1.2 which is the same from official example. This is not an issue for me as long as it is not related to my problem.

jimisdrpc avatar Nov 11 '20 22:11 jimisdrpc

Sorry for terrible format. I added the code inside the "Insert Code" space created. I tried re-edit but I couldn't. Basically I added two main files: build.gradle that I downloaded from micronaut.launch and I have just added io.grpc:protoc-gen-grpc-kotlin and the build.gradle from official example. Hopefully who reads it can understand it.

jimisdrpc avatar Nov 11 '20 22:11 jimisdrpc

For reference: https://stackoverflow.com/questions/64803461/although-i-can-generate-the-stubs-with-io-grpcprotoc-gen-grpc-kotlin-i-cant-im

jeffscottbrown avatar Nov 12 '20 13:11 jeffscottbrown

@jeffbrown in case it helps, I tried copied all build.gradle from example provided to the project generated from Micronaut.Launch and I had the same issue. On another hand, the inverse works: I copied my proto file to the example provided and it works. This gives me a clue that there is something extra in the example provided that is mmissed in project generated from micronaut.launch

jimisdrpc avatar Nov 12 '20 14:11 jimisdrpc

I tried copied all build.gradle from example provided to the project generated from Micronaut.Launch and I had the same issue

If the example you are talking about is the one at github.com/micronaut-projects/micronaut-grpc/tree/c8ec599ad1b696d432efecb10145105074f3d26a/examples/hello-world-kotlin, be aware that project is using Micronaut 2.0.0.M3 and your project is using Micronaut 2.1.3.

jeffscottbrown avatar Nov 12 '20 16:11 jeffscottbrown

@jeffbrown thanks. Well, my project is generated from Micronaut Laucher. I assume this is the best source for starting a fresh and up-to-date project with proper dependencie versions. Have you successfuly downloaded a grpc application from micronaut launcher and created successfully a proto and its endpoint in Kotlin? If so than would mind to share with me?

jimisdrpc avatar Nov 13 '20 00:11 jimisdrpc

modify sourceSets in build.gradle file

sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/grpckt' // load the generated kotlin file
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

morrle avatar Nov 13 '20 06:11 morrle

@morrle thanks, but same issue. May I ask you if you can share with me a very simple project evoulated from Micronaut Launcher with Kotlin? I am really stuck.

I tried again. The steps are:

1 - I generated grpcApplication/kotlin from Micronaut Launcher. 2 - added these lines in build.gradle

plugins {
 ...
    id "com.google.protobuf" version "0.8.13"
...
dependencies {
...
    implementation("io.grpc:grpc-kotlin-stub:0.1.2")
...
sourceSets {
    main {
        java {
...
            srcDirs 'build/generated/source/proto/main/grpckt' // load the generated kotlin file
...
protobuf {
...
            grpckt {}}

3 - I tried to implement the proto example which came with autogenerated project

import javax.inject.Singleton

@Singleton
class MyEndpoint : GrpcdemoServiceGrpcKt.GrpcdemoServiceCoroutineImplBase() {
    override suspend fun send(request: GrpcdemoRequest): GrpcdemoReply {
        return GrpcdemoReply.newBuilder().setMessage("test").build()
    }
}

Here is the error when I tried gradle clean build

image

Here is the whole build.gradle

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.4.10"
    id "org.jetbrains.kotlin.kapt" version "1.4.10"
    id "org.jetbrains.kotlin.plugin.allopen" version "1.4.10"
    id "com.github.johnrengelman.shadow" version "6.1.0"
    id "io.micronaut.application" version '1.0.5'

    id "com.google.protobuf" version "0.8.13"
}

version "0.1"
group "com.mybank"

repositories {
    mavenCentral()
    jcenter()
}

micronaut {
    testRuntime "junit5"
    processing {
        incremental true
        annotations "com.mybank.*"
    }
}

dependencies {
    implementation("io.micronaut:micronaut-validation")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("javax.annotation:javax.annotation-api")

    implementation("io.grpc:grpc-kotlin-stub:0.1.2")

    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
    testImplementation("io.micronaut:micronaut-http-client")

}

mainClassName = "com.mybank.ApplicationKt"
java {
    sourceCompatibility = JavaVersion.toVersion('11')
}

compileKotlin {
    kotlinOptions {
        jvmTarget = '11'
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = '11'
    }
}



sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'

            srcDirs 'build/generated/source/proto/main/grpckt' // load the generated kotlin file
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.13.0" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.32.1" }

        grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:0.1.2" }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}

            grpckt {}}
    }
}

The rest is exactly what I downloaded from micronaut launcher.

jimisdrpc avatar Nov 13 '20 13:11 jimisdrpc

Well, I am still looking forward to find a solution for this. Normally, we expected from frameworks launchers/initializers to be precise with dependencies.

jimisdrpc avatar Nov 15 '20 00:11 jimisdrpc

@jimisdrpc seems to be an issue specific to Kotlin, will investigate ... thanks for the report

graemerocher avatar Nov 16 '20 08:11 graemerocher

So the statement "Normally, we expected from frameworks launchers/initializers to be precise with dependencies." is inaccurate in that frameworks and launchers typically only include libraries and features when a stable release of a library is present and you are not using the raw output from the launcher, you are modifying the code to introduce protoc-gen-grpc-kotlin which is in an alpha/beta/prerelease state

graemerocher avatar Nov 16 '20 09:11 graemerocher

@graemerocher ok, you are right. I didn't know protoc-gen-grpc-kotlin wasn't in stable version. As far as I can see, looking at https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-kotlin I don't get this point. BTW, I can give a try with Java e remove this dependencie.

jimisdrpc avatar Nov 16 '20 13:11 jimisdrpc

@jimisdrpc Have you considered grpc-kotlin by Google? I used it with a Spring Boot project, and although not GA yet, it is doing a good job so far.

asarkar avatar Nov 17 '20 09:11 asarkar

@asarkar , thanks for suggestion. Good to know. I will give a try. It is worth to me as long as it runs well with Micronaut.

jimisdrpc avatar Nov 17 '20 11:11 jimisdrpc

@jimisdrpc i think this last error is because in your file .proto you write the "sendMethod" with the first letter in lower case, you should try write like this "SendMethod", i have the same error and i fix it with this change.

marcoscouto avatar Dec 15 '20 22:12 marcoscouto

@marcoscouto Naming in proto file is a convention, not a rule. If not following the convention results in an error, it’s a bug.

asarkar avatar Dec 15 '20 23:12 asarkar

@graemerocher Since December 8th I can say https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-kotlin/1.0.0 is in stable version, right?

Do you see any point in this build.gradle?

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.10"
    id("org.jetbrains.kotlin.kapt") version "1.4.10"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.4.10"
    id("com.github.johnrengelman.shadow") version "6.1.0"
    id("io.micronaut.application") version "1.2.0"
    id("com.google.protobuf") version "0.8.13"
}

version = "0.1"
group = "com.tolearn"

repositories {
    mavenCentral()
    jcenter()
}

micronaut {
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("com.tolearn.*")
    }
}

dependencies {
    implementation("io.micronaut:micronaut-validation")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("javax.annotation:javax.annotation-api")
    implementation("io.micronaut:micronaut-http-client")
    implementation("io.micronaut:micronaut-tracing")

    implementation("io.grpc:protoc-gen-grpc-kotlin:1.0.0")

    runtimeOnly("io.jaegertracing:jaeger-thrift")
    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
}


application {
    mainClass.set("com.tolearn.ApplicationKt")
}

java {
    sourceCompatibility = JavaVersion.toVersion("11")
}

tasks {
    compileKotlin {
        kotlinOptions {
            jvmTarget = "11"
        }
    }
    compileTestKotlin {
        kotlinOptions {
            jvmTarget = "11"
        }
    }


}

sourceSets {
    main {
        java {
            srcDirs("build/generated/source/proto/main/grpc")
            srcDirs 'build/generated/source/proto/main/grpckt'
            srcDirs("build/generated/source/proto/main/java")
        }
    }
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.14.0" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.33.1" }
        grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0" }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
            grpckt {}
        }
    }
}

My goal is use protoc-gen-grpc-kotlin so the stubs will be generated in Kotlin with supend function (Coroutine context).

jimisdrpc avatar Dec 20 '20 21:12 jimisdrpc

Here is an example build.gradle.kts that works for me with Kotlin 1.4.30:

import com.google.protobuf.gradle.*
plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.30"
    id("org.jetbrains.kotlin.kapt") version "1.4.30"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.4.10"
    id("com.github.johnrengelman.shadow") version "6.1.0"
    id("io.micronaut.application") version "1.3.3"
    id("com.google.protobuf") version "0.8.13"
}

version = "0.1"
group = "tmp"

val kotlinVersion=project.properties.get("kotlinVersion")
repositories {
    mavenCentral()
    jcenter()
}

micronaut {
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("tmp.*")
    }
}

dependencies {
    implementation("io.micronaut:micronaut-validation")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
    implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    implementation("io.micronaut:micronaut-runtime")
    implementation("io.micronaut.grpc:micronaut-grpc-runtime")
    implementation("io.grpc:grpc-kotlin-stub:1.0.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin")
    testImplementation("io.micronaut:micronaut-http-client")
}


application {
    mainClass.set("tmp.ApplicationKt")
}

java {
    sourceCompatibility = JavaVersion.toVersion("1.8")
}

tasks {
    compileKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    compileTestKotlin {
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }


}
sourceSets {
    main {
        java {
            srcDirs("build/generated/source/proto/main/grpc")
            srcDirs("build/generated/source/proto/main/java")
        }
    }
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.14.0"
    }
    plugins {
        id("grpc") {
            artifact = "io.grpc:protoc-gen-grpc-java:1.33.1"
        }
        id("grpckt") {
            artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0:jdk7@jar"
        }        
    }
    generateProtoTasks {
        ofSourceSet("main").forEach {
            it.plugins {
                // Apply the "grpc" plugin whose spec is defined above, without options.
                id("grpc")
                id("grpckt")
            }
        }
    }
}

graemerocher avatar Feb 12 '21 12:02 graemerocher