0

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.

qtips
  • 625
  • 6
  • 17

2 Answers2

0

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:

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • As I wrote in my question, I am configuring using annotations and not web.xml which is optional in Java EE 7. Also, I believe that I do not need WEB-INF/lib because I am not using it. I have only used AnnotationConfiguration because I configure everything from annotations, and I want to understand why it is not working. My theory is that this setup should work for all Java EE 7 supporting servlet containers. – qtips May 31 '16 at 19:57
  • Even if you don't have the actual file, you still need the configuration, as that's how the webapp metadata is setup. without the configuration, no metadata, no knowledge, about the setup, etc. The metadata is what's built up over all of these configurations. only Annotation/Plus/Env are optional, the rest are required. – Joakim Erdfelt May 31 '16 at 20:02
  • When adding WebInfConfiguration, Jetty starts reading my annotation configuration :) I still argue that I should not need WebXmlConfiguration because I see that Jetty uses a default web.xml which sets up some built-in servlets. But without it, Jetty does not serve my index.html. Why must Jetty add its own web.xml in order to support static content? I think I got confused by Jettys WebAppContext; It is used for serving web apps exclusivly (in contrast with ServletContextHandler), but does not support standard Java EE configuration out of the box, without using setConfigurations. – qtips May 31 '16 at 20:30
  • the `webdefault.xml` is a standard servlet requirement. Jetty isn't Java EE btw, its just a Web Container that supports Servlets. – Joakim Erdfelt May 31 '16 at 20:45
  • I see from http://www.eclipse.org/jetty/documentation/current/jetty-javaee.html that Jetty supports Servlet 3.1. Further, I read in the Servlet 3.1 spec http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-spec/index.html (ch. 8.2.1) that web.xml is optional if annotations are used. This is also reflected by the Oracle example that I used to in my question. Anyway, I guess I learned that I should not assume strict compliance with the Java EE specs. – qtips May 31 '16 at 21:21
  • also, I did not find `webdefault.xml` in any Java EE servlet documentation as it seems to be a Jetty specific configuration file. – qtips May 31 '16 at 21:25
  • 1
    @qtips Jetty's interpretation of the default web application requirements (like the welcome list, Default servlet, jsp support urls, TRACE exclusion, etc) is to establish these defaults via a `webdefault.xml`, which can be found as a resource in the `jetty-webapp-.jar` file, and be overridden in the [`WebAppContext.setDefaultsDescriptor()`](http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/webapp/WebAppContext.html#setDefaultsDescriptor-java.lang.String-). Jetty is a subset of a subset of Java EE, and that label requires TCK testing (which is incompat with OSS) – Joakim Erdfelt May 31 '16 at 21:41
0

After discussing with Joakim Erdfelt I looked further into Tomcat and I understand that Jetty is probably not the best container to test different kinds of Java EE web features. Jetty realises the support of Java EE in other ways than Glasshfish and Tomcat, which in turn requires different setup approach, which is elaborated in the discussion under Joakims answer.

After testing with Tomcat using the following snippet

public class MyServer {

public static void main(String[] args) throws Exception {
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);

    tomcat.addWebapp("","webapp"); 

    tomcat.start();
    tomcat.getServer().await();
    }

 }

it first looked like Tomcat did not require any web.xml, but after further investigating, Tomcat uses a default web.xml if one is not provided. This web.xml includes a <welcome-file>index.html</welcome-file> and a DefaultServlet which serves static content. This approach is similar to Jettys, and I assume this is also how Glassfish works. So lesson learned is that there is always some kind of DefaultServlet that lurks in the background of Java EE web servers, which does some of the magic work. Even if web.xml is optional for the user of the server, the server still provides it behind the scenes to make it self work.

qtips
  • 625
  • 6
  • 17
  • Interesting interpretation of what I said. :-( – Joakim Erdfelt Jun 01 '16 at 21:35
  • I am sorry if I misunderstood you :( jetty seems to be very flexible and probably also provides better performance. However this flexibility makes it harder to setup. You cannot simply follow the official Java EE tutorials setups and expect it to work. You have to add jetty specific setup to make it work. Of course this is not a big issue, but when learning the Java EE and sevlets it can be a little confusing. – qtips Jun 02 '16 at 06:25
  • If you are focused on Java EE, I would recommend using [TomEE](http://tomee.apache.org/), not plain Tomcat (as its in the same narrow subset scope as Jetty is). Note however, that TomEE isn't terribly good at being embedded (mainly due to constraints put on it by Java EE) – Joakim Erdfelt Jun 02 '16 at 13:53