4

Short: I have a project that provides a war artifact which includes a servlet with annotations but no web.xml. If i try to use the war in jetty i always get only the directory listing of the war content but not the servlet execution.

Any idea?

Long story: My servlets look like this

package swa;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet( asyncSupported = false, urlPatterns={"/*"})
public class ServletX extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // Set response content type
        response.setContentType("text/html");

        // Actual logic goes here.
        PrintWriter out = response.getWriter();
        out.println("<h1>Hi there..</h1>");
    }

}

So nothing special i guess. When i use mvn jetty:run everything is fine. After ensuring this the project is packed into a war-archive.

This war archive is used within another project that has to set up jetty within code. This is how its done:

        String jettyPort = properties.getProperty("jetty.port", "8080");
        Server server = new Server();

        ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory());
        httpConnector.setPort(Integer.parseInt(jettyPort));
        httpConnector.setAcceptQueueSize(2);
        httpConnector.setHost("0.0.0.0");
        httpConnector.setIdleTimeout(30000);
        server.setConnectors(new Connector[] { httpConnector });

        WebAppContext wacHandler = new WebAppContext();
        wacHandler.setContextPath("/admin");
        wacHandler.setWar("swa-0.0.1-SNAPSHOT.war");
        wacHandler.setConfigurationDiscovered(true);

        server.setHandler(wacHandler);

        server.start();

When executing this project the logs tell me that the war is found. But if i open the url http://localhost:8080/admin i only see the listing of the war content (instead of 'Hi there').

Can someone point me to my failure?

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
kDot
  • 43
  • 1
  • 6

2 Answers2

10

Updated - Aug 2021

This process has changed starting in Jetty 10.0.0 (this includes Jetty 11.0.0)

The existence of the jetty-annotations-<ver>.jar on the classpath is enough to enable the annotation and bytecode scanning for your servlets and websocket layers.

The WebAppContext.setConfiguration(...) method should no longer be used.

The old example project has been archived and replaced with

https://github.com/jetty-project/embedded-servlet-server

There are examples for ...

Servlet API Version Jetty Version New Branch
3.1 Jetty 9.4.x embedded-servlet-server : jetty-9.4.x
4.0 Jetty 10.x embedded-servlet-server : jetty-10.0.x
5.0 Jetty 11.x embedded-servlet-server : jetty-11.0.x

Original Answer - Sept 2014

You need to define the WebAppContext configurations appropriately (and in the correct order).

    wacHandler.setConfigurations(new Configuration[]
    { 
        new AnnotationConfiguration(), 
        new WebInfConfiguration(), 
        new WebXmlConfiguration(), 
        new MetaInfConfiguration(), 
        new FragmentConfiguration(),
        new EnvConfiguration(), 
        new PlusConfiguration(), 
        new JettyWebXmlConfiguration() 
    });

Don't forget to add the jetty-annotations.jar.

This is from the EmbedMe.java example for use with Servlet 3.1 found at

https://github.com/jetty-project/embedded-servlet-3.1/

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • 1
    @JoseMartinez The question was about embedded jetty, using jetty xml isn't terribly appropriate in that situation. Now, if using jetty-distribution, this list of Configurations is managed for you based on your configured modules (see jetty.home, jetty.base, and start.ini documentation) – Joakim Erdfelt Mar 03 '15 at 14:17
  • 1
    @JoakimErdfelt Is it possible to do this without a war file? –  Mar 18 '17 at 18:16
  • @niklabaz annotation scanning is a feature of Servlet spec WebApps, if you don't have a WAR (or an exploded/expanded webapp directory) then its not a WebApp, and hence no scanning. (technical reason: this is a webapp metadata and servlet lifecycle + ordering issue. no formal webapp, none of that exists) – Joakim Erdfelt Mar 20 '17 at 13:28
6

In addition to adding the necessary configurations as stated in the abovementioned answer, one needs to force Jetty to scan the current project compiled classes, which Jetty ignores by default. To do that, just call the following on your WebAppContext:

context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/classes/.*")

Please see the complete example here (in Kotlin): https://github.com/mvysny/vaadin-on-kotlin/blob/master/vok-example-crud/src/test/java/com/github/vok/example/crud/Server.kt

This works awesome and discovers all @WebServlets, @WebListeners and others.

Martin Vysny
  • 3,088
  • 28
  • 39
  • The `org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern` is for scanning Server Container jars on the server classloader. This does not apply to `WebAppContext` based classes and jars (like in `WEB-INF/lib`) – Joakim Erdfelt Aug 17 '21 at 13:44