Can't get Jetty to scan for annotated classes
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("/");
...
![Ryan Silva](https://i.stack.imgur.com/geLle.jpg?s=256&g=1)
Ryan Silva
Updated on June 17, 2022Comments
-
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.