0

I am trying to inject a custom handler to jetty.

I have written the handler in my application code which is packaged as a war.

package com.foo.bar

import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.handler.AbstractHandler

// scalastyle:off println
class CustomJettyHandler extends AbstractHandler {
  override def handle(target: String, baseRequest: Request,
                      request: HttpServletRequest, response: HttpServletResponse): Unit = {

    println("This is a custom jetty handler")
  }
}
// scalastyle:on println

I have then injected this handler in the jetty.xml file:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
........
........
<Call name="insertHandler">
    <Arg>
      <New id="CustomJettyHandler" class="com.foo.bar.CustomJettyHandler"/>
    </Arg>
</Call>
........

I am now running jetty in standalone mode. Note that I am passing the location where CustomJettyHandler.class resides to jetty-start.jar.

java -server -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -jar lib/jetty-start.jar OPTIONS=All --lib=lib/* --lib=webapps/root/WEB-INF/classes/com/foo/bar/* etc/jetty.xml etc/jetty-jmx.xml --debug

In my application logs, I can see jetty loading my custom handler to its classpath but then eventually failing because of ClassNotFoundException. Can someone point out where could this be going wrong?

.......
rawlibref = webapps/root/WEB-INF/classes/com/foo/bar/*
expanded = webapps/root/WEB-INF/classes/com/foo/bar/*
getPaths('webapps/root/WEB-INF/classes/com/foo/bar/*')
Using relative path pattern: glob:**/webapps/root/WEB-INF/classes/com/foo/bar/*
Found [webapps/root/WEB-INF/classes/com/foo/bar/CustomJettyHandler.class]  /Users/...path_to_application.../webapps/root/WEB-INF/classes/com/foo/bar/CustomJettyHandler.class
Adding classpath component: /Users/...path_to_application.../webapps/root/WEB-INF/classes/com/foo/bar/CustomJettyHandler.class
.......
.......
.......
URLClassLoader.url[33] = file:/Users/...path_to_application.../webapps/root/WEB-INF/classes/com/foo/bar/CustomJettyHandler.class
Loaded 34 URLs into URLClassLoader
class org.eclipse.jetty.xml.XmlConfiguration - 9.4.24.v20191120
Command Line Args: /var/folders/z5/dmt38gq54kxcrrzpgvbl5m_c0000gp/T/start_6046998329479549547.properties /Users/...path_to_application.../etc/jetty.xml /Users/...path_to_application.../etc/jetty-jmx.xml
2020-05-27 14:46:13.676:INFO::main: Logging initialized @477ms to org.eclipse.jetty.util.log.StdErrLog
2020-05-27 14:46:13.934:INFO:oeju.TypeUtil:main: JVM Runtime does not support Modules
2020-05-27 14:46:14.007:WARN:oejx.XmlConfiguration:main: Config error at <Call name="insertHandler"><Arg>|      <New id="CustomJettyHandler" class="com.foo.bar.CustomJettyHandler"/>|    </Arg></Call> java.lang.ClassNotFoundException: com.foo.bar.CustomJettyHandler in file:///Users/...path_to_application.../etc/jetty.xml
2020-05-27 14:46:14.007:WARN:oejx.XmlConfiguration:main:
java.security.PrivilegedActionException: java.lang.ClassNotFoundException: com.foo.bar.CustomJettyHandler
    at java.security.AccessController.doPrivileged(Native Method)
    at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:1837)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jetty.start.Main.invokeMain(Main.java:218)
    at org.eclipse.jetty.start.Main.start(Main.java:491)
    at org.eclipse.jetty.start.Main.main(Main.java:77)
Caused by:
java.lang.ClassNotFoundException: com.foo.bar.CustomJettyHandler
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.eclipse.jetty.util.Loader.loadClass(Loader.java:64)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.newObj(XmlConfiguration.java:1028)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.itemValue(XmlConfiguration.java:1638)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.value(XmlConfiguration.java:1539)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.access$500(XmlConfiguration.java:369)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration$AttrOrElementNode.getList(XmlConfiguration.java:1768)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration$AttrOrElementNode.getList(XmlConfiguration.java:1744)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:919)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:512)
    at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:454)
    at org.eclipse.jetty.xml.XmlConfiguration.configure(XmlConfiguration.java:354)
    at org.eclipse.jetty.xml.XmlConfiguration.lambda$main$0(XmlConfiguration.java:1874)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:1837)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jetty.start.Main.invokeMain(Main.java:218)
    at org.eclipse.jetty.start.Main.start(Main.java:491)
    at org.eclipse.jetty.start.Main.main(Main.java:77)
jose praveen
  • 1,298
  • 2
  • 10
  • 17
Arpit Jain
  • 61
  • 1
  • 9

1 Answers1

1

Where did you get this command line from?

java -server \
  -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog\
  -jar lib/jetty-start.jar\
  OPTIONS=All \
  --lib=lib/* \
  --lib=webapps/root/WEB-INF/classes/com/foo/bar/* \
  etc/jetty.xml \
  etc/jetty-jmx.xml \
  --debug

That's not valid for use with Jetty 9.x's start.jar

Some advice

  • don't use XML directly on the java command line, that's the responsibility of the start.jar and the jetty-home module system (order is SUPER IMPORTANT).
    Your choices of etc/jetty.xml and etc/jetty-jmx.xml is an incomplete list of xmls. (you are missing all dependent XML files)
  • don't edit the standard Jetty XML files, leave them be, otherwise you'll complicate upgrades later. Use XML to inject your behavior instead (see below for example)
  • OPTIONS is not supported by Jetty 9.x (that's old school Codehaus / Jetty 6 behavior)
  • Your usage of --lib= is discouraged, it only supports fully qualified paths to jars or directories with exploded class trees (not relative paths, no globs supported).
  • -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog is a harsh way of setting up logging. Create a jetty-logging.properties files and make sure it's present on the classpath.

Example contents of jetty-logging.properties

org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.deploy.LEVEL=DEBUG

Do this instead.

Create an injection based XML for your new handler.

my-handler.xml

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

<Configure id="Server" class="org.eclipse.jetty.server.Server">
    <Call name="insertHandler">
        <Arg>
            <New id="CustomJettyHandler" class="com.foo.bar.CustomJettyHandler" />
        </Arg>
    </Call>
</Configure>

Next create your jetty-base directory properly (for jetty's start.jar)

# Create your "jetty-base" directory
$ mkdir /path/to/myjettybase
$ cd /path/to/myjettybase

# Establish the basic files / directories / modules that you want to use
# You can find the configuration in start.ini or start.d/*.ini
$ java -jar /path/to/jetty-home/start.jar --add-to-start=http,jmx,deploy,ext,resources

# Copy your custom handler JAR into place
$ cp /path/to/my-handlers.jar /path/to/myjettybase/lib/ext/
# Copy your custom handler XML into place
$ cp /path/to/my-handler.xml /path/to/myjettybase/etc/
# Ensure that the custom handler XML is loaded into the jetty instance at the right point in the XML load order by declaring it to be used in a custom INI
$ mkdir start.d
$ echo "etc/my-handler.xml" >> start.d/my-handlers.ini

# Copy your jetty-logging.properties into place
$ cp /path/to/my-jetty-logging.properties /path/to/myjettybase/resources/jetty-logging.properties

# verify that your configuration looks sane (including the server classpath)
$ cd /path/to/myjettybase
$ java -jar /path/to/jetty-home/start.jar --list-config

# run your instance
$ cd /path/to/myjettybase
$ java -jar /path/to/jetty-home/start.jar

But that's not all, seeing as you seem to want to use jetty-home from a maven-style project (or project layout), you can do that too!

An example project showing this can be found at ...

https://github.com/jetty-project/servlet-error-page-handling

That maven project is also a valid jetty-base directory suitable for execution by a jetty-home archive somewhere else on your machine.

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • I copied `myHandler` related files in my jetty-home directory and the `demo.war` file to `/jetty-home/webapps` directory. I can see my-handler in `--list-config`: ` 1: 1.0-SNAPSHOT | ${jetty.base}/lib/ext/jetty-custom-handler-1.0-SNAPSHOT.jar Jetty Active XMLs: ${jetty.base}/etc/my-handler.xml` But running the server from jetty home gives `java.lang.IllegalStateException: No Method: | | on class org.eclipse.jetty.server.Server` – Arpit Jain May 28 '20 at 08:27
  • I even tried doing this from `jetty-base` and that also results in: `java.lang.IllegalStateException: No Method: on class org.eclipse.jetty.server.Server Caused by: java.lang.NoSuchMethodException: insertHandler at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:1008) at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:963)` – Arpit Jain May 28 '20 at 08:42
  • "handler in my application code which is packaged as a war" is your handler IN the WAR file, or on the Server classpath? – Joakim Erdfelt May 28 '20 at 15:07
  • There are two ways I have tried this: 1. Writing the handler class in a module which is packaged as a war, and then passing the `target/classes` directory of this module to jetty using `--lib` parameter. 2. Writing the handler class in a module which is packaged as a jar, and then copying the jar in `/lib/ext/ directory within `jetty-base` directory. Both the cases show correct config but throw exceptions on jetty startup. Here's a GitHub repo which reproduces both these issues. I have also added the error details in README. https://github.com/calvincodes/custom-handler-jetty – Arpit Jain May 28 '20 at 18:56
  • Were you able to reproduce the error using the repository which I have mentioned in the previous comment? https://github.com/calvincodes/custom-handler-jetty @joakim-erdfelt – Arpit Jain Jun 08 '20 at 18:25