Can't get Jetty to scan for annotated classes

13,816

Solution 1

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.

Solution 2

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)));

Solution 3

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();

   }

}

Solution 4

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

Solution 5

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("/");
 ...
Share:
13,816
Ryan Silva
Author by

Ryan Silva

Updated on June 17, 2022

Comments

  • Ryan Silva
    Ryan Silva about 2 years

    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 [email protected]: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.