Cannot load io.grpc.ManagedChannelProvider in custom plugin

Answered

I have a plugin that needs grpc to work. I wrote a separate java library to handle all the grpc connection stuff, and import it in my plugin project as a jar.

The jar includes a key function:

ManagedChannelBuilder.forAddress("172.21.42.105", 8082)

It will load io.grpc.ManagedChannelProvider through ServiceLoader.

I have the following build.gradle.kts in my plugin project:

plugins {
id 'java'
id 'org.jetbrains.intellij' version '0.7.3'
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
}

group 'org.binance.android.plugin'
version '1.0.3'

repositories {
mavenCentral()
}

if (!hasProperty('StudioCompilePath')) {
throw new GradleException("No StudioCompilePath value was set, please create gradle.properties file")
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
implementation fileTree(dir:"libs",includes:["*jar"])

compileOnly fileTree(dir: "$StudioCompilePath/plugins/android/lib", include: ['android.jar', 'sdk-tools.jar'])

implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-native-mt")
implementation("io.grpc:grpc-netty-shaded:$grpcVersion")
implementation("io.grpc:grpc-protobuf:$grpcVersion")
implementation("io.grpc:grpc-stub:$grpcVersion")
implementation("io.grpc:grpc-api:$grpcVersion")
}

// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
alternativeIdePath StudioCompilePath
updateSinceUntilBuild = false
buildSearchableOptions.systemProperty("idea.is.internal", "")
}

test {
useJUnitPlatform()
}

runIde {
jvmArgs '-Xmx4G'
}

task(verifySetup) {
doLast {
def ideaJar = "$StudioCompilePath/lib/idea.jar"
if (!file(ideaJar).exists()) {
throw new GradleException("$ideaJar not found, set StudioCompilePath in gradle.properties")
}
}
}

compileJava.dependsOn verifySetup

configurations {
all {
resolutionStrategy.sortArtifacts(ResolutionStrategy.SortOrder.DEPENDENCY_FIRST)
}
}

The grpcVersion is 1.36.0 and the grpcKotlinVersion is 1.2.1

I know the AS has grpc relevant jars under its lib directory and the version is 1.21.1. I use ResolutionStrategy.SortOrder.DEPENDENCY_FIRST to ensure that my plugin uses version 1.36.0. I checked the build plugin and the relevant libs are packaged into it:

However, I encountered the following exception when I run my plugin:

java.util.ServiceConfigurationError: io.grpc.ManagedChannelProvider: io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider not a subtype

It's really wired because I've packaged 1.36.0 version into my plugin. The ClassLoader used to load the provider is PluginClassLoader, the reasonable explanation is that the NettyChannelProvider is not loaded from my plugin, but I don't know why.

3 comments
Comment actions Permalink

Hi Yann Cebron,

Thanks for the suggestion. I'm facing a similar issue. I gave the serviceloader strategy (the one you linked in your comment) a try, but I was still facing the same issue.

I believe this problem has been posted in other threads as well and all point to an issue with the classes/loaders between the grpc libraries included in the plugin vs the grpc library that is shaded and included by IntelliJ itself.

What I see is that the IntelliJ shaded gRPC lib includes a META-INF/services definition to load the shaded implementations, hence why the exceptions talk about an `io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider` class.

The only workaround (temporary I hope :) ) I found that works was to "shade" that class inside my plugin itself in the meantime to disable that in my gRPC server loader. This I believe is less than ideal, but works in the meantime (I'm in early development stages of the plugin).

package io.grpc.netty.shaded.io.grpc.netty;

import io.grpc.ServerBuilder;
import io.grpc.ServerProvider;

public class NettyServerProvider extends ServerProvider {
@Override
protected boolean isAvailable() {
return false;
}

@Override
protected int priority() {
return 0;
}

@Override
protected ServerBuilder<?> builderForPort(int i) {
return null;
}
}

By adding this class in my plugin project, this one gets loaded instead of the one from IntelliJ and the error doesn't occur any longer, and I can start a gRPC server in my plugin successfully after that using the grpc libs I defined in my build.gradle file.

Yann Cebron - do you see another workaround/solution that might work in this case? If you feel that the ServiceLoader approach you linked should definitely work please let me know and I'll give it another go.

Thanks!

0
Comment actions Permalink

Sorry, no better/other idea from my side

 

0

Please sign in to leave a comment.