Search code examples
javaplayframework-2.0nettyselendroid

Why does Play throw NoSuchMethodError for Netty's HttpRequest.headers() with Selendroid?


Ok so I had my localhost:9000 working just fine one week ago and I am getting this error ever since I moved my project to Eclipse. Even when I have the code in its most basic form, it produces a Netty error that is caught by Play.

Here is my error:

 [info] play - Application started (Prod)
 [info] play - Listening for HTTP on /0.0.0.0:9000
 [error] p.nettyException - Exception caught in Netty
 java.lang.NoSuchMethodError: org.jboss.netty.handler.codec.http.HttpRequest.headers()Lorg/jboss/netty/handler/codec/http/HttpHeaders;
 at play.core.server.netty.PlayDefaultUpstreamHandler.getHeaders(PlayDefaultUpstreamHandler.scala:366) ~[na:na]
 at play.core.server.netty.PlayDefaultUpstreamHandler.messageReceived(PlayDefaultUpstreamHandler.scala:87) ~[na:na]
 at com.typesafe.netty.http.pipelining.HttpPipeliningHandler.messageReceived(HttpPipeliningHandler.java:62) ~[na:na]
 at org.jboss.netty.handler.codec.http.HttpContentDecoder.messageReceived(HttpContentDecoder.java:100) ~[na:na]
 at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) ~[na:na]
 [error] p.nettyException - Exception caught in Netty
 java.lang.NoSuchMethodError: org.jboss.netty.handler.codec.http.HttpRequest.headers()Lorg/jboss/netty/handler/codec/http/HttpHeaders;
 at play.core.server.netty.PlayDefaultUpstreamHandler.getHeaders(PlayDefaultUpstreamHandler.scala:366) ~[na:na]
 at play.core.server.netty.PlayDefaultUpstreamHandler.messageReceived(PlayDefaultUpstreamHandler.scala:87) ~[na:na]
 at com.typesafe.netty.http.pipelining.HttpPipeliningHandler.messageReceived(HttpPipeliningHandler.java:62) ~[na:na]
 at org.jboss.netty.handler.codec.http.HttpContentDecoder.messageReceived(HttpContentDecoder.java:100) ~[na:na]
 at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) ~[na:na]
 [error] p.nettyException - Exception caught in Netty
 java.lang.NoSuchMethodError: org.jboss.netty.handler.codec.http.HttpRequest.headers()Lorg/jboss/netty/handler/codec/http/HttpHeaders;
 at play.core.server.netty.PlayDefaultUpstreamHandler.getHeaders(PlayDefaultUpstreamHandler.scala:366) ~[na:na]
 at play.core.server.netty.PlayDefaultUpstreamHandler.messageReceived(PlayDefaultUpstreamHandler.scala:87) ~[na:na]
 at com.typesafe.netty.http.pipelining.HttpPipeliningHandler.messageReceived(HttpPipeliningHandler.java:62) ~[na:na]
 at org.jboss.netty.handler.codec.http.HttpContentDecoder.messageReceived(HttpContentDecoder.java:100) ~[na:na]
 at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) ~[na:na] 

Here is the basic form code:

public class Application extends Controller /*implements Runnable*/ {

public static RequestBody print;
//private static RemoteWebDriver driver;
//private static int ok;

public static Result index() {
    return ok(index.render("Your new application is ready."));
}

// Step that prints the object once the JSON is received
@BodyParser.Of(BodyParser.Json.class)
public static Result receive(){
    JsonNode json = request().body().asJson();
    if(json == null)
        // As long as the json is null it will display this
        return badRequest("Expecting data");
    else{
        RequestBody body = request().body();
           print = body;
           return ok("Data received.\n" + body.asJson());
    }
}

public static Result show(){
    if(print == null){
        return ok("Expecting data.");
    }
    //(new Thread(new Application())).start();
    return ok(print.asJson());
}
}

Solution

  • tl;dr Use evicted command, exclude and force() in the build with very helpful reports under target/resolution-cache/reports/. Don't forget about sbt-dependency-graph.

    As said in Exclude specific transitive deps:

    Sometimes it takes a bit of detective work to figure out which transitive deps to exclude.

    I've experienced the very similar issue and the solution was to use proper dependency of Netty library (or rather exclude one to let assembly pass).

    [microservice]> evicted
    [info] Updating {file:/Users/jacek/work/.../}microservice...
    [info] Resolving jline#jline;2.12 ...
    [info] Done updating.
    [warn] There may be incompatibilities among your library dependencies.
    [warn] Here are some of the libraries that were evicted:
    [warn]  * xml-apis:xml-apis:1.0.b2 -> 1.4.01 (caller: xerces:xercesImpl:2.11.0, dom4j:dom4j:1.6.1)
    [warn]  * io.netty:netty:(3.6.6.Final, 3.6.3.Final) -> 3.9.3.Final (caller: com.typesafe.netty:netty-http-pipelining:1.1.2, com.typesafe.play:play_2.11:2.3.7, org.apache.cassandra:cassandra-all:2.0.8, com.netflix.astyanax:astyanax-cassandra:1.56.48)
    [warn]  * joda-time:joda-time:1.6.2 -> 2.3 (caller: com.typesafe.play:play-json_2.11:2.3.7, com.netflix.astyanax:astyanax-core:1.56.48, com.typesafe.play:play_2.11:2.3.7, com.netflix.astyanax:astyanax-cassandra:1.56.48, com.netflix.astyanax:astyanax-recipes:1.56.48, com.netflix.astyanax:astyanax-thrift:1.56.48)
    [warn]  * jline:jline:1.0 -> 2.11 (caller: com.tinkerpop:gremlin-groovy:3.0.0.M6, org.apache.cassandra:cassandra-all:2.0.8)
    [success] Total time: 5 s, completed Feb 20, 2015 6:42:14 PM
    

    As you may have noticed in the above snippet, I was facing dependency version mismatch between three different versions of Netty - 3.6.6.Final, 3.6.3.Final and 3.9.3.Final.

    I needed 3.6.6.Final to be chosen so I used force() for the version that I explicitly added to the build (using build.sbt):

    libraryDependencies += "io.netty" % "netty" % "3.6.3.Final" force()
    

    With the above line in the build, executing evicted showed:

    [warn]  * io.netty:netty:(3.9.3.Final, 3.6.6.Final) -> 3.6.3.Final (caller: com.typesafe.netty:netty-http-pipelining:1.1.2, org.apache.cassandra:cassandra-all:2.0.8, com.typesafe.play:play_2.11:2.3.7, com.netflix.astyanax:astyanax-cassandra:1.56.48, my.company:microservice_2.11:0.6.1)
    

    The build is for my.company:microservice_2.11:0.6.1 that appeared in the above output (the very last library).

    Sometimes, however, you should use exclude to remove conflicting dependencies as follows:

    libraryDependencies ~= { _ map(m => m.exclude("commons-logging", "commons-logging")) }