Jetty Maven plugin ClassNotFoundException on my ErrorHandler

I have created a custom error handler following this reciepe

When I stand om my project and do the command ls target/classes/foo/bar/error/MyErrorHandler*

Then it shows: target/classes/foo/bar/error/MyErrorHandler.class

I have added the a custom jetty.xml with the content:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
    <Set name="errorHandler">
        <New class="foo.bar.error.MyErrorHandler"/>
    </Set>
</Configure>

Here is my pom.xml

<plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <configuration>
                   <classesDirectory>target/classes</classesDirectory>
                    <httpConnector>
                        <port>9999</port>
                    </httpConnector>
                    <jettyXml>src/test/resources/jetty-errorhandler.xml</jettyXml>
                </configuration>

When I run mvn jetty:run -X

I get the following error:

[DEBUG] parse: file:/home/vagrant/git/seopp/seopp-kunde/src/test/resources/jetty-errorhandler.xml
[DEBUG] parsing: sid=file:/home/vagrant/git/seopp/seopp-kunde/src/test/resources/jetty-errorhandler.xml,pid=null
[DEBUG] resolveEntity(-//Jetty//Configure//EN, http://www.eclipse.org/jetty/configure.dtd)
[DEBUG] Redirected entity http://www.eclipse.org/jetty/configure.dtd --> jar:file:/var/cache/maven_repository/org/eclipse/jetty/jetty-xml/9.2.11.v20150529/jetty-xml-9.2.11.v20150529.jar!/org/eclipse/jetty/xml/configure_9_0.dtd
[WARNING] Config error at <Set name="errorHandler">
        <New class="foo.bar.error.MyErrorHandler"/>
    </Set>
[INFO] Jetty server exiting.

Caused by: java.lang.ClassNotFoundException: foo.bar.error.MyErrorHandler
    at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
    at org.eclipse.jetty.util.Loader.loadClass(Loader.java:86)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.nodeClass(XmlConfiguration.java:364)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.newObj(XmlConfiguration.java:754)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.itemValue(XmlConfiguration.java:1125)

How come it cannot find my MyErrorHandle file? It is in my target/classes directory with the correct path.

Answers


I eventually made it working, even though I still wonder whether a nicer solution exists. To me, the solution seems more related to Maven plugin classpath management in general and not to Jetty itself. The Maven plugins add to their classpath what it is actually defined as dependencies of the plugin. It obviously also applies to the Jetty Plugin (unless it also implements some other mechanism enabled via configuration, still to be discovered).

I created a sample project and used the configuration you supplied, having the same ClassNotFoundException. I solved it adding as dependency of the Jetty plugin the project itself (the only solution?), that is, the one providing the custom handler class.

Below the working POM:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample</groupId>
    <artifactId>jetty-error</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.2.8.v20150217</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <webApp>
                        <contextPath>/jetty-error</contextPath>
                    </webApp>
                    <httpConnector>
                        <port>7777</port>
                    </httpConnector>
                    <classesDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</classesDirectory>
                    <jettyXml>src/main/resources/jetty.xml</jettyXml>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-util</artifactId>
                        <version>9.2.8.v20150217</version>
                    </dependency>
                    <dependency>
                        <groupId>com.sample</groupId>
                        <artifactId>jetty-error</artifactId>
                        <version>0.0.1-SNAPSHOT</version>
                        <scope>system</scope>
                        <systemPath>${basedir}/lib/jetty-error-0.0.1-SNAPSHOT.jar</systemPath>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.2.8.v20150217</version>
        </dependency>
    </dependencies>
</project>

NOTE: my packaing is a war, but it shouldn't make a difference. I also used a system scope for the plugin dependency in this case, never mind, it was just to quickly make it work (I had an issue with my company maven repository and maven didn't look it up in the cache firstly, but I didn't want to investigate further, it was not the goal of this exercise).

So, for a quick test, I temporarily created a jar out of the project and added to a lib directory, but it could be easily automated as part of the build (I really wanted to prove that it needed to be part of the Jetty Maven Plugin and not of the Jetty server to make it work via jetty:run).

Below my simple error handler:

package com.sample.jetty;

import java.io.IOException;
import java.io.Writer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ErrorHandler;

public class MyErrorHandler extends ErrorHandler {

    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        response.getOutputStream().println("error!");
    }

    public void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
            throws IOException {
        writer.write("error!");
    }

    public void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
            throws IOException {
        writer.write("error!");
    }

    public void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
            throws IOException {
        writer.write("error!");
    }

    public void writeErrorPageBody(HttpServletRequest request, Writer writer, int code, String message,
            boolean showStacks) throws IOException {
        writer.write("error!");
    }

    public void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message, String uri)
            throws IOException {
        writer.write("error!");
    }

    public void writeErrorPageStacks(HttpServletRequest request, Writer writer) throws IOException {
        writer.write("error!");
    }

}

No class not found exception using it with your configuration. I also set it as global, via the following jetty config (jetty.xml):

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
"http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <Call name="addBean">
    <Arg>
      <New class="com.sample.jetty.MyErrorHandler">
        <Set name="server"><Ref refid="Server" /></Set>
      </New>
    </Arg>
  </Call>
</Configure>

And then typing a wrong address (i.e. http://localhost:7777/whatever), I then got my error! message from the custom error handler (it works!).

So, conclusion is: the class must be part of the plugin classpath because indeed when running jetty:run we are running a Maven plugin.

My sample above can be improved further (generating the jar/loading the jar in a more standard maven approach/folder (that is, not system scope); use a multimodule maven project, having the error handler in a module and the jetty plugin in another module in order to have a dependency on that module; etc.), but the principle would probably be the same. It was a challenging and nice investigation. Pity tutorials and documentation online don't help much on this topic.


Need Your Help

Dissallowing Javascript-disabled users to browse a web application

javascript jquery html browser

I have a JS/jQuery intensive website and certain very important functionality works on JS and Jquery (features without which the site wont function properly). I want to disallow users who have JS-

Using LINQ to find item in a List but get "Value cannot be null. Parameter name: source"

c# linq

When using LINQ to get data from a list I encounter this error. How can this be fixed?