twitter / finatra

Fast, testable, Scala services built on TwitterServer and Finagle
https://twitter.github.io/finatra/
Apache License 2.0
2.27k stars 405 forks source link

IncompatibleClassChangeError when updating from 20.3.0 -> 20.6.0 #543

Closed zachkirlew closed 4 years ago

zachkirlew commented 4 years ago

After upgrading finatra-http from 20.3.0 to 20.6.0 I've started getting the following error: Server does not implement the requested interface com.twitter.util.CloseAwaitably0.

Expected behaviour

No errors, server should start up healthy as before.

Actual behaviour

When attempting to run Main and test code that uses my HttpServer implementation I receive the following error:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Class com.adevinta.engprod.openpr.OpenPrServerMain$ does not implement the requested interface com.twitter.util.CloseAwaitably0
cacoco commented 4 years ago

@zachkirlew thanks for the issue! Can you provide any code details which will reproduce the issue? This is a bit minimal to triage. Are you in Scala or Java, are you using Maven or SBT? Java JDK version, etc. etc. Additionally, can you verify that all of the other necessary com.twitter dependencies are all on the same 20.6.0 version? Thanks again.

zachkirlew commented 4 years ago

Sorry! I'm using Scala (2.12.1) + Gradle 6.4.1, JDK 1.8. All com.twitter dependencies are on the same 20.6.0 version: e.g.

buildscript {
    ext {
        scalaMainVersion = '2.12'
        scalaVersion = "$scalaMainVersion.10"
    }
}

plugins {
    id 'idea'
    id 'scala'
}

ext {
    finatraVersion = '20.6.0'
}

configurations {
    scapegoat
}

dependencies {
    implementation "org.scala-lang:scala-library:$scalaVersion"
    implementation "com.twitter:finatra-http_$scalaMainVersion:$finatraVersion"
    implementation "com.twitter:twitter-server-logback-classic_$scalaMainVersion:$finatraVersion"
    implementation 'ch.qos.logback:logback-classic:1.2.3'

    implementation configurations.scapegoat
    implementation "net.logstash.logback:logstash-logback-encoder:6.2"
    implementation "com.47deg:github4s_$scalaMainVersion:0.24.0"

    implementation "org.typelevel:cats-effect_$scalaMainVersion:2.1.3"
    implementation "io.catbird:catbird-effect_$scalaMainVersion:$finatraVersion"
    implementation "io.catbird:catbird-util_$scalaMainVersion:$finatraVersion"

    testImplementation "junit:junit:4.13"
    testImplementation "org.scalatest:scalatest_$scalaMainVersion:3.0.8"
    testImplementation "org.scalacheck:scalacheck_$scalaMainVersion:1.13.4"
    testImplementation "com.twitter:finatra-http_$scalaMainVersion:$finatraVersion:tests",
            "com.twitter:finatra-jackson_$scalaMainVersion:$finatraVersion:tests",
            "com.twitter:inject-server_$scalaMainVersion:$finatraVersion:tests",
            "com.twitter:inject-app_$scalaMainVersion:$finatraVersion:tests",
            "com.twitter:inject-core_$scalaMainVersion:$finatraVersion:tests",
            "com.twitter:inject-modules_$scalaMainVersion:$finatraVersion:tests",
            "com.google.inject.extensions:guice-testlib:4.2.2",
            "org.mockito:mockito-scala_$scalaMainVersion:1.8.0"

    scapegoat "com.sksamuel.scapegoat:scalac-scapegoat-plugin_$scalaMainVersion:1.3.0"
}

My Main looks like this:

object ServerMain extends Server

class Server extends HttpServer {
  override def modules: Seq[Module] = {
    Seq(PrometheusModule, ConfigModule, SecretsModule, GithubModule)
  }

  override def jacksonModule: Module = CustomMapperModule

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .filter[LoggingMDCFilter[Request, Response]]
      .filter[TraceIdMDCFilter[Request, Response]]
      .filter[CommonFilters]
      .add[GithubController]
      .add[PrometheusController]
  }
}
enbnt commented 4 years ago

As of the 20.6.0 release, c.t.app.App and c.t.server.TwitterServer no longer implement CloseAwaitably (which in turn implements CloseAwaitably0[Unit]). They now implement CloseOnceAwaitably, which can be seen in these changes. This should have been marked as a breaking change in the case where something is typed specifically to expect CloseAwaitably. I apologize that this was not noted in the CHANGELOG for the release.

I hope this information helps in narrowing down the cause.

zachkirlew commented 4 years ago

You say "in the case where something is typed specifically to expect CloseAwaitably". Can you give me an example of this?

What is the suggested solution in this case? Simply implementing the requested interface causes another error:

class Server inherits conflicting members:
  method ready in trait CloseOnceAwaitably0 of type (timeout: com.twitter.util.Duration)(implicit permit: com.twitter.util.Awaitable.CanAwait)Server.this.type  and
  method ready in trait CloseAwaitably0 of type (timeout: com.twitter.util.Duration)(implicit permit: com.twitter.util.Awaitable.CanAwait)Server.this.type
(Note: this can be resolved by declaring an override in class Server.);
mosesn commented 4 years ago

@zachkirlew can you print your dependency graph and share it with us? I'm also going to try reproducing it on my end.

mosesn commented 4 years ago

@zachkirlew I was unable to reproduce your problem. I have a slightly simpler project, but I think it should still work similarly:

import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter

object ServerMain extends Server

class Server extends HttpServer {
  def configureHttp(router: HttpRouter): Unit = ()
}

If you're not pulling in an older TwitterServer somehow, maybe you've cached a compilation target? Maybe try cleaning your gradle caches?

zachkirlew commented 4 years ago

So I checked my dependency graph and found that I was importing something that was using a 20.1.0 version of some com.twitter modules. I've updated it now and all the com.twitter dependencies are 20.6.0.

However I'm now getting this error:

Could not initialize class com.twitter.finagle.http.HttpMuxer$
java.lang.NoClassDefFoundError: Could not initialize class com.twitter.finagle.http.HttpMuxer$
mosesn commented 4 years ago

@zachkirlew strange. can you try adding an explicit dep on com.twitter finagle-http? You should get it transitively, but maybe you're not?

zachkirlew commented 4 years ago

@mosesn tried but it had no effect unfortunately

mosesn commented 4 years ago

NoClassDefFound typically indicates that a classfile that was used at compile-time is not available at runtime. This one is a bit more unusual because HttpMuxer$ hasn't changed recently. HttpMuxer$ refers to the companion object for HttpMuxer.

I pulled down the com.twitter finagle-http 20.6.0 JAR and looked to see if it contained the classfile, but it does seem to contain it.

    6986  Defl:N     2831  60% 06-24-2020 00:57 af7b21e4  com/twitter/finagle/http/HttpMuxer$.class

I'm afraid this is another build issue on your side. Can you inspect your dependency graph again and double check that you have the right version of finagle-http on your classpath? It might also be useful to try simplifying your buildgraph and your project until you're no longer able to reproduce your issue.

One possible strategy to experiment quickly with this without having to change your code would be to run the repl against your modified buildgraph and try compiling / interpreting code that uses the subset of deps that you have in your modified buildgraph.

mosesn commented 4 years ago

@zachkirlew I hope you were able to figure out your problem 🙂. since it doesn't seem to be related to finatra (except that you're depending on finatra) I'm going to close this ticket. If you have other problems, please let us know!