21

I have an application with an embedded jetty server which I'm starting up like this (placed in main() and launched with eclipse):

Server server = new Server(port);

WebAppContext context = new WebAppContext();
context.setResourceBase("web/");
context.setDescriptor("web/WEB-INF/web.xml");
context.setConfigurations(new Configuration[]{
            new AnnotationConfiguration(), new WebXmlConfiguration(),
            new WebInfConfiguration(), new TagLibConfiguration(),
            new PlusConfiguration(), new MetaInfConfiguration(),
            new FragmentConfiguration(), new EnvConfiguration()});

context.setContextPath("/");
context.setParentLoaderPriority(true);
server.setHandler(context);
server.start();
server.join();

My web.xml looks like this (empty for now, I'm not sure if I can remove it completely):

<web-app 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    metadata-complete="false"
    version="3.0">
</web-app>

And I have a simple class set up like this:

import java.io.IOException;

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(urlPatterns={"/test"})
public class TestServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException  {
        request.getRequestDispatcher("/WEB-INF/html/index.html").forward(request,response);
    }
}

My application works fine when I use traditional servlet mappings in web.xml. But when I remove the web.xml mappings and use annotations, I only get 404s. It doesn't look like it's scanning for annotations at all. The console looks like this:

2012-08-01 17:40:37.021:INFO:oejs.Server:jetty-8.1.5.v20120716
2012-08-01 17:40:37.227:INFO:oejpw.PlusConfiguration:No Transaction manager found - if your webapp requires one, please configure one.
2012-08-01 17:40:37.294:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/me/project/web/}
2012-08-01 17:40:37.547:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/me/project/web/}
2012-08-01 17:40:37.547:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/me/project/web/}
2012-08-01 17:40:37.547:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/me/project/web/}
2012-08-01 17:40:37.641:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080

Some things that I have checked already from my research:

  • servlet-3.0 jar is in the classpath
  • metadata-complete set to false in web.xml
  • I made sure to include AnnotationConfiguration in web app context

I've run out of ideas and am about to just revert back to the old web.xml, but it's killing me why I can't get this to work.

Daniel Rucci
  • 2,822
  • 2
  • 32
  • 42
Ryan Silva
  • 925
  • 2
  • 9
  • 17
  • Did Joakim Erdfelt's answer actually work? – ManRow Apr 09 '13 at 05:02
  • Not for me (Jetty 9.0). Same as @user1569803, and all dependencies in place. I'm investigating alternate routes. – LSerni Oct 14 '13 at 00:04
  • Unfortunately I no longer have access to the project which had this issue, so I can't verify the answer for my situation. If some old co-workers try the solution and have success, I'll mark the answer for them. – Ryan Silva Oct 18 '13 at 01:19

7 Answers7

8

Update: June 2021

The example project has been updated to be Jetty version neutral.

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

It has branches for specific versions of Jetty now.

The older example projects have been archived.

Update: June 2015

The example project has been updated for Jetty 9 and Servlet 3.1

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

Original Answer:

From your description, and a sample project I whipped up using your code, you are doing everything correctly.

Sample project: https://github.com/jetty-project/embedded-servlet-3.0

In order for this to work, you'll want the following (only mentioning this as your question didn't include this detail)

  • JDK 1.6+
  • jetty-webapps jar (+ dependencies) from Jetty 8.1.x (or newer)
  • jetty-annotations jar (+ dependencies) from Jetty 8.1.x (or newer)

Just from these limited requirements you'll see the following list of dependencies present.

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building sample-webapp 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ sample-webapp ---
[INFO] com.company.sample:sample-webapp:war:1-SNAPSHOT
[INFO] +- org.eclipse.jetty.orbit:javax.servlet:jar:3.0.0.v201112011016:provided
[INFO] +- org.eclipse.jetty:jetty-webapp:jar:8.1.5-SNAPSHOT:test
[INFO] |  +- org.eclipse.jetty:jetty-xml:jar:8.1.5-SNAPSHOT:test
[INFO] |  |  \- org.eclipse.jetty:jetty-util:jar:8.1.5-SNAPSHOT:test
[INFO] |  \- org.eclipse.jetty:jetty-servlet:jar:8.1.5-SNAPSHOT:test
[INFO] |     \- org.eclipse.jetty:jetty-security:jar:8.1.5-SNAPSHOT:test
[INFO] |        \- org.eclipse.jetty:jetty-server:jar:8.1.5-SNAPSHOT:test
[INFO] |           +- org.eclipse.jetty:jetty-continuation:jar:8.1.5-SNAPSHOT:test
[INFO] |           \- org.eclipse.jetty:jetty-http:jar:8.1.5-SNAPSHOT:test
[INFO] |              \- org.eclipse.jetty:jetty-io:jar:8.1.5-SNAPSHOT:test
[INFO] \- org.eclipse.jetty:jetty-annotations:jar:8.1.5-SNAPSHOT:test
[INFO]    +- org.eclipse.jetty:jetty-plus:jar:8.1.5-SNAPSHOT:test
[INFO]    |  +- org.eclipse.jetty.orbit:javax.transaction:jar:1.1.1.v201105210645:test
[INFO]    |  \- org.eclipse.jetty:jetty-jndi:jar:8.1.5-SNAPSHOT:test
[INFO]    |     \- org.eclipse.jetty.orbit:javax.mail.glassfish:jar:1.4.1.v201005082020:test
[INFO]    |        \- org.eclipse.jetty.orbit:javax.activation:jar:1.1.0.v201105071233:test
[INFO]    +- org.eclipse.jetty.orbit:javax.annotation:jar:1.1.0.v201108011116:test
[INFO]    \- org.eclipse.jetty.orbit:org.objectweb.asm:jar:3.1.0.v200803061910:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.771s
[INFO] Finished at: Fri Aug 10 18:17:46 MST 2012
[INFO] Final Memory: 6M/180M
[INFO] ------------------------------------------------------------------------

It is quite likely that you are just missing a dependency or JDK requirement.

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • I had the same problem and your answer solved it; I was missing jetty-annotations on classpath AND I didn't have the appropriate `Configuration` set (AnnotationConfiguration). Thanks! – Merchuk Hul Aug 26 '14 at 07:45
8

AnntationConfiguration class scans annotations via its scanForAnnotations(WebAppContext) method. In the method AnnotationConfiguration class scans following path.

  • container jars
  • WEB-INF/classes
  • WEB-INF/libs

So if you want your servlet classes in your production code(i.e. the sources in the directory src/main/java) to be scanned, add your production code into the WebAppContext's metadata as WEB-INF/classes.

Try the code bellow, for adding your code into WebAppContext's metadata.

URL classes = getClass()
        .getProtectionDomain()
        .getCodeSource()
        .getLocation();

WebAppContext context = new WebAppContext();
context.getMetaData()
    .setWebInfClassesDirs(
        Arrays.asList(Resource.newResource(classes)));
mike_neck
  • 176
  • 1
  • 2
5

I had this same problem, but after many reads I got the solution! IMPORTANT: remember to have in your build path the following jars:

jetty-all-9.0.6.v20130930.jar

jetty-annotations-9.0.6.v20130930.jar

org.objectweb.asm-3.1.0.v200803061910.jar

javax.servlet-api-3.0.1.jar

jetty-plus-9.0.6.v20130930.jar

public class Main {

   public static void main(String[] args) throws Exception {

       //Create the server
       Server server = new Server(8080);

       ClassList clist = ClassList.setServerDefault(server);

       //clist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration",    "org.eclipse.jetty.plus.webapp.PlusConfiguration");
       clist.addBefore(JettyWebXmlConfiguration.class.getName(), AnnotationConfiguration.class.getName());

       //Here is the trick to scan current classpath!
       webapp.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", 
            ".*/build/classes/");

       webapp.setContextPath("/");
       webapp.setResourceBase("./WebContent");


       server.setHandler(webapp);

       server.start();
       server.join();

   }

}
Lucas Batistussi
  • 2,283
  • 3
  • 27
  • 35
  • More details on [ContainerIncludeJarPattern](http://eclipse.org/jetty/documentation/current/configuring-webapps.html#container-include-jar-pattern) – ursa May 16 '15 at 17:41
  • for me it works though the webapp variable is not defined! I got it working because the webapp is an instance of org.eclipse.jetty.webapp.WebAppContext – Dirk Schumacher Nov 11 '16 at 09:58
1

Based on the previous example provided by Joakim, I've uploaded a modified version that supports annotations in an embedded jetty without packaging the project into a war file. This project is ready to deploy to Heroku just running:

$java -cp target/classes:"target/dependency/*" com.example.Launcher

If you are interested in the details, the important lines are these ones from com.example.Launcher:

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

// Important! make sure Jetty scans all classes under ./classes looking for annotations. 
//'Classes' directory is generated running 'mvn package'
context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*/classes/.*");

You can change accordingly the JarPattern depending on the classes you want to scan for annotations.

Here you have the full example: https://github.com/pablormier/embedded-jetty-annotations-example

Pablo R. Mier
  • 719
  • 1
  • 7
  • 13
  • 1
    The line context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*/classes/.*"); was the key for me. Just setup the configurations didn't work. Thanks. – reinaldoluckman Sep 25 '14 at 16:14
1

I have come up with a different approach rather specific to Jetty & Spring. Instead of letting Jetty scan for classes, I manually called initializer classes in startContext() method of a custom Jetty ServletContextHandler. Code is as follows:

public class CustomServletContextHandler extends ServletContextHandler {

   @Override
   protected void startContext() throws Exception {

       SpringServletContainerInitializer initer = new SpringServletContainerInitializer();

        HashSet<Class<?>> classes = new HashSet<>();
        // Add annotated classes here such as
        //classes.add(SpringSecurityInitializer.class);
        //classes.add(SpringWebInitilializer.class);

        try {
            initer.onStartup(classes, this.getServletContext());
        }catch(Exception e)
        {
            e.printStackTrace();
        }

        super.startContext();
    }
}

And then when creating context, use custom handler:

 ServletContextHandler ctx = new CustomServletContextHandler();
 ctx.setContextPath("/");
 ...
mll5
  • 193
  • 2
  • 10
1

You can

  1. provide EmptyResource.INSTANCE if you do not need static resources

  2. add AnnotationConfiguration and override scanForAnnotations method, adding classpath resource to WebAppContext Metadata, possibly with path to root package for a scan

    WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        webapp.setBaseResource(EmptyResource.INSTANCE);
        webapp.setConfigurations(new Configuration[]{
                new AnnotationConfiguration(){
                    @Override
                    protected void scanForAnnotations(WebAppContext context) throws Exception {
                        Resource classPathResource =    Resource.newResource(EmbeddedServerApp.class.getResource("/my/learn/jetty/servlet").toURI());
                        context.getMetaData().addContainerResource(classPathResource);
                        super.scanForAnnotations(context);
                    }
                },
        });
        server.setHandler(webapp);
        server.start();
    
Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73
aelor
  • 60
  • 5
0

In our case these lines helped in Jetty startup code:

    ClassList cl = Configuration.ClassList.setServerDefault(server);
    cl.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");
ssasa
  • 1,616
  • 1
  • 18
  • 30