square / okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
https://square.github.io/okhttp/
Apache License 2.0
45.92k stars 9.16k forks source link

java.lang.NoSuchFieldError: Companion when in a kotlin only module / kotlin("jvm") plugin #5818

Closed vinnorman closed 4 years ago

vinnorman commented 4 years ago

I have a modularised project, where the api layer is separated into its own module. As this has no dependency on the Android SDK, I have made it a kotlin only module. This has the kotlin jvm plugin as shown below in the build file. This results in a java.lang.NoSuchFieldError: Companion error when I try to use MockWebServer (only this, everything else works fine).

Using kotlin dsl in the build file, the build.gradle.kts file looks like this:

plugins {
    kotlin("jvm")
    kotlin("kapt")
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("com.squareup.retrofit2:retrofit:2.7.0")
    implementation("com.squareup.retrofit2:converter-moshi:2.7.0")
    implementation("com.squareup.moshi:moshi-kotlin:1.9.2")
    implementation("com.squareup.okhttp3:logging-interceptor:4.4.0")
    testImplementation("com.squareup.okhttp3:mockwebserver:4.4.0")
    testImplementation("junit:junit:4.13")
    kapt("com.squareup.moshi:moshi-kotlin-codegen:2.7.0")
    // There are various other libraries, but I'm including the only ones I feel are relevant 
    // in case my problem is to do with conflicting versions of libraries
}

And here is the snippet of my unit test. The line where I instantiate MockWebServer() fails with the NoSuchFieldError: Companion:

class LoginServiceTest {

private val mockWebServer: MockWebServer = MockWebServer()

And the stack trace:

java.lang.NoSuchFieldError: Companion

    at okhttp3.internal.Util.<clinit>(Util.kt:69)
    at okhttp3.mockwebserver.MockWebServer.<init>(MockWebServer.kt:101)
    at com.mobilleo.data.webapi.service.core.user.LoginServiceTest.<init>(LoginServiceTest.kt:29)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:250)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:260)
    at org.junit.runners.BlockJUnit4ClassRunner$2.runReflectiveCall(BlockJUnit4ClassRunner.java:309)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Process finished with exit code 255

After various experiments, I have discovered the following:

Working build.gradle.kts:

plugins {
    id("com.android.library")
    kotlin("android")
    kotlin("kapt")
 }

android {

    compileSdkVersion(29)
    // etc. etc. Android config, that all seems a bit pointless just to get MockWebServer working

My instinct tells me it's some difference between the way kotlin android compiles the Kotlin, with Companion objects or static methods etc., compared with when just using the kotlin jvm compiler, but I confess to not knowing enough about what happens 'under the hood' with all this.

swankjesse commented 4 years ago

The mockwebserver and OkHttp module need to be on the same version. If you add a dependency on okhttp 4.4.0 it should fix. Or use the okhttp-bom feature and get your versions managed centrally.

vinnorman commented 4 years ago

I get the same error adding this dependency: implementation("com.squareup.okhttp3:okhttp:4.4.0") Also doesn't explain why it works on kotlin("android") and not kotlin("jvm").

Any ideas?

vinnorman commented 4 years ago

Ok the plot thickens a little. The specific dependency that breaks it is actually another module. So I have a 'models' module, that I add as follows:

 implementation(project(":models"))

And that module's dependencies look like this:

plugins {
    kotlin("jvm")
    kotlin("kapt")
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61")
    implementation("com.squareup.moshi:moshi-kotlin:1.9.2")
    kapt("com.squareup.moshi:moshi-kotlin-codegen:1.9.2")
}

And just having the implementation(project(":models")) definitely breaks it, and causes the NoSuchFieldError: Companion

So unless I'm doing something wrong here, this is definitely a bug, and I think the issue should be reopened.

vinnorman commented 4 years ago

When I remove this dependency from my ":models" module, the error stops:

 implementation("com.squareup.moshi:moshi-kotlin:1.9.2")

So, to be specific, MockWebServer returns a NoSuchFieldError: Companion when there is a dependency to another module that has this dependency: implementation("com.squareup.moshi:moshi-kotlin:1.9.2")

I have managed to reproduce exactly this behaviour in a brand new project. Any guidance would be appreciated, thanks!

ryantoussaint commented 4 years ago

@vinnorman I ran into java.lang.NoSuchFieldError: Companion as well. My situation was that I was using a certain version of okhttp in my project, but I have another dependency that internally it was using a different version of okhttp (so 2 different versions). I ended up using gradle's dependencyInsight to track down the different versions.

Usage: cd into your subproject and run: gradle -q dependencyInsight --dependency okhttp3

Sample Output: com.squareup.okhttp3:okhttp:3.10.0 -> 4.4.1

From there you can try excluding a dependency or forcing a particular version in your gradle file

rwst commented 2 years ago

Apparently this can be triggered even in a blank project that just uses HttpsURLConnection. When I try to install the org.jetbrains.kotlin.plugin.serialization plugin:

org.gradle.api.plugins.InvalidPluginException: An exception occurred applying plugin request [id: 'org.jetbrains.kotlin.android']
    at ...
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin 'org.jetbrains.kotlin.android'.
...
Caused by: org.gradle.api.reflect.ObjectInstantiationException: Could not create an instance of type org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension.
...
Caused by: java.lang.NoSuchFieldError: Companion
    at org.jetbrains.kotlin.gradle.dsl.ToolchainSupport$Companion.createToolchain$kotlin_gradle_plugin(ToolchainDsl.kt:33)
    at org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension.<init>(KotlinProjectExtension.kt:69)
    at org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension.<init>(KotlinProjectExtension.kt:109)
...