qtips qtips - 6 months ago 48
Java Question

Embedded Jetty with exploded war, using annotated config

I am learning Java EE 7 Servlets and tried to deploy

hello2
example from Java EE 7 tutorial using embedded Jetty (v 9.3.7) with little success.
hello2
consists of two servlets and an image file. The configuration is annotated and the project does not have any web.xml.

Following the
WebAppContext
part from embedded Jetty examples I created this main class to initiate my embedded server:

public class MyServer {

public static void main(String[] args) throws Exception {
Server server = new Server(8080);
String webappPath = new File(MyServer.class.getProtectionDomain().getCodeSource().getLocation().getFile())
.getParentFile().getParentFile().getAbsolutePath();

WebAppContext webapp = new WebAppContext(webappPath, "");

webapp.setConfigurations(new Configuration[]{
new AnnotationConfiguration()});

server.setHandler(webapp);
server.start();
server.join();
}
}


As I understand, since Jetty is a Java EE web container, it should be able to serve the example Serlvet project as-is, and I simply need to point to the war folder structure. The following is the structure of the project:

-- hello2
\-- src
\-- main
+-- java
│   +-- MyServer.java
│   \-- javaeetutorial
│   \-- hello2
│   +-- GreetingServlet.java
│   \-- ResponseServlet.java
\-- webapp
+-- WEB-INF
│   \-- classes
│   +-- MyServer.class
│   \-- javaeetutorial
│   \-- hello2
│   +-- GreetingServlet.class
│   \-- ResponseServlet.class
+-- index.html
\-- resources
\-- images
\-- duke.waving.gif


The
hello2
example code can be found here. Here are some parts of
GreetingServlet


@WebServlet("/greeting")
public class GreetingServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
....


and
ResponseServlet


@WebServlet("/response")
public class ResponseServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
....


The files are compiled to
hello2/webapp/classes/
thus making the
webapp
folder an exploded WAR. The index.html is something I added just to test whether Jetty picks it up. The result is that I get error 404 when I visit localhost:8080, localhost:8080/greeting or localhost:8080/response

If I add
WebXmlConfiguration
with
webapp.setConfigurations()
and then set the resource base like
webapp.setResourceBase(webappPath)
, I manage to get into the Jetty's static file server. This is because Jetty then uses a default
web.xml
which adds its own servlets for file serving purpose to the server. But even then my annotated servlets are not picked up.

The way I got Jetty to read the annotated servlet configuration is by setting the
WEB-INF
directory explicitly using
WebAppContext.getMetadata().setWebInfClassesDirs()
:

webapp.getMetaData().setWebInfClassesDirs(
Arrays.asList(Resource.newResource(
MyServer.class.getProtectionDomain().getCodeSource().getLocation())));


Then, the servlets respond as expected, but this this does not serve my
index.html
or the image file. I also set the resource base to no use. So what I want is Jetty to serve my web application without
web.xml
, and by simply pointing it to the exploded WAR directory. Clearly I am missing something.

Answer

Using ...

webapp.setConfigurations(new Configuration[]{
        new AnnotationConfiguration()});

will undo all of the existing important configurations and only have the AnnotationConfiguration enabled.

No wonder it isn't working for you, with that setup, the configuration that loads WEB-INF/web.xml is missing, the configuration that uses WEB-INF/lib is missing, etc.

You have to modify the existing configuration list appropriately, and there's plenty of examples out there to show you this.

Since you didn't specify if you have annotations that use JNDI, or exist in web-fragments, or come from outside of the webapp context (such as the container itself), the exact configuration you need is tough to specify.

See the https://github.com/jetty-project/embedded-servlet-3.1 project for a complete project that does this.

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

This is a general setup, which is the most common, but might expose you to extra configuration you might not want.

Even if you don't want the other components, you still need to leave them be for a discovered webapp. Otherwise you have a 100% manual webapp, that you specifically call .addServlet() and .addFilter() etc on.

You should probably use this syntax instead.

private void enableAnnotationScanning(Server server)
{
    Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(server);
    classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
            "org.eclipse.jetty.annotations.AnnotationConfiguration");
}

as this will modify the existing list of Configurations to just add the AnnotationConfiguration

If you want to see other examples of this format, look at the following example projects:

Comments