Ryan Silva
Ryan Silva

Reputation: 965

Can't get Jetty to scan for annotated classes

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:

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.

Upvotes: 21

Views: 15061

Answers (7)

Joakim Erdfelt
Joakim Erdfelt

Reputation: 49495

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.

Upvotes: 8

aelor
aelor

Reputation: 60

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

Upvotes: 1

mll5
mll5

Reputation: 193

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("/");
 ...

Upvotes: 1

ssasa
ssasa

Reputation: 1615

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

Upvotes: 0

mike_neck
mike_neck

Reputation: 176

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

Upvotes: 8

Pablo R. Mier
Pablo R. Mier

Reputation: 747

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

Upvotes: 1

Lucas Batistussi
Lucas Batistussi

Reputation: 2343

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

   }

}

Upvotes: 5

Related Questions