Jetty and the NoSuchMethodError

Jetty is a wonderful servlet engine you can embed in your application. For my first experiment I started with a simple program to use it as HTML server:

public final class Runner {

    private final Server jetty;

    public Runner() {
        jetty = new Server(4040);
    }

    public void start() throws Exception {
        jetty.setHandler(new WebAppContext("src/main/webapp", "/"));
        jetty.start();
    }

    public static void main(String[] args) throws Exception {
        ClasspathMonitor.addAsShutdownHook();
        Runner runner = new Runner();
        runner.start();
    }

}

I have a simple HTML page which is located in the directory src/main/webapp. After running this litte wrapper from Eclipse I could see the HTML page under http://localhost:4040/.

Now I want more - I want to use it as JSP engine. Before I did a small test to use as servlet engine which was successful (I created only a small web.xml with a InitServlet). To use Jetty as JSP environment I must include some libraries which are listed in Embedding+Jetty. I put the libraries in my classpath, created a simple JSP and started Jetty again. When I access the JSP page I got a HTTP ERROR 500 and the following stacktrace:

HTTP ERROR 500

Problem accessing /rt/classpath.jsp. Reason:

    org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer.(Lorg/eclipse/jdt/internal/compiler/env/IBinaryType;)V

Caused by:

java.lang.NoSuchMethodError: org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer.(Lorg/eclipse/jdt/internal/compiler/env/IBinaryType;)V
	at org.apache.jasper.compiler.JDTCompiler$1.findType(JDTCompiler.java:214)
	at org.apache.jasper.compiler.JDTCompiler$1.findType(JDTCompiler.java:183)
	at org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.askForType(LookupEnvironment.java:119)
	at org.eclipse.jdt.internal.compiler.lookup.PackageBinding.getTypeOrPackage(PackageBinding.java:178)
	at org.eclipse.jdt.internal.compiler.lookup.Scope.getPackage(Scope.java:2149)
	at org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference.getTypeBinding(QualifiedTypeReference.java:62)
	at org.eclipse.jdt.internal.compiler.ast.TypeReference.resolveType(TypeReference.java:141)
	at org.eclipse.jdt.internal.compiler.ast.TypeReference.resolveSuperType(TypeReference.java:104)
	at org.eclipse.jdt.internal.compiler.lookup.ClassScope.findSupertype(ClassScope.java:1107)
	at org.eclipse.jdt.internal.compiler.lookup.ClassScope.connectSuperclass(ClassScope.java:767)
	at org.eclipse.jdt.internal.compiler.lookup.ClassScope.connectTypeHierarchy(ClassScope.java:947)
	at org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope.connectTypeHierarchy(CompilationUnitScope.java:258)
	at org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.completeTypeBindings(LookupEnvironment.java:195)
	at org.eclipse.jdt.internal.compiler.Compiler.beginToCompile(Compiler.java:301)
	at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:315)
	at org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:387)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:288)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:267)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:255)
	at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:556)
	at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:293)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:291)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
	at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
	at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:389)
	at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
	at org.mortbay.jetty.Server.handle(Server.java:324)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:535)
	at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:865)
	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:539)
	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
	at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
	at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)

Powered by Jetty://

What the hell is the problem here? My first suspicion was that it could be a classpath problem. But how can I confirm it?

to be cont'd...

Jetty and the NoSuchMethodError (2)

How can be classpath problems like this NoSuchMethodError be found? The answer to this question can be found with the ClasspathMonitor provided by PatternTesting. All you need is to add patterntesting-rt-xxx.jar to your classpath and to register the ClasspathMonitor at JMX:

public static void main(String[] args) throws Exception {
    ClasspathMonitor.registerAsMBean();
    Runner runner = new Runner();
    runner.start();
}

If you start the application you have to add the options "-Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote" for JMX otherwise you would not see your application in the list of started java processes when you open JMX console.

Start your program and then the jconsole. The jconsole is since Java 5 part of the JDK. When you open the MBeans tab you can find the ClasspathMonitor MBean under the patterntesting folder. As you can see on the screenshot the ClasspathMonitor found an incompatible class org.apache.jasper.compiler.Localizer, i.e. this class was found more than once in the classpath (in jasper-compiler-5.5.9.jar and jasper-runtime-5.5.9.jar) but with different versions.

I don't know why I used this version of the jasper. After I upgraded to jasper 5.5.15 I saw my JSP page without an error.

to be cont'd...

Jetty and the NoSuchMethodError (3)

Last time we saw how we can use the ClasspathMonitor of PatternTesting and the jconsole of the JDK to find classpath problems. But what if the program crashes immediately after you started it? You have no chance to use the jconsole here!

For this case the ClasspathMonitor can be added as shutdown hook:

    public static void main(String[] args) throws Exception {
    ClasspathMonitor.addAsShutdownHook();
    ...
}

Each time your program crashes a text file "cpmonxxxxx.txt" is dumped the temp directory. If you open this file and look for the string "IncompatibleClasses" you will see an entry like this:

...

=== IncompatibleClasses ===
class org.apache.jasper.compiler.Localizer

=== IncompatibleClasspath ===
/Users/oliver/.m2/repository/tomcat/jasper-compiler/5.5.9/jasper-compiler-5.5.9.jar
/Users/oliver/.m2/repository/tomcat/jasper-runtime/5.5.9/jasper-runtime-5.5.9.jar

...

There are two other useful side effects if you register the ClasspathMonitor as shutdown hook:

Do you know the Lava Flow Anti Pattern? PatternTesting can help you to find dead classes (and also dead methods) in your code. But this is another story