Java Development News:

Pondering About JSR-135, the New Servlet 3.0 Specification

By Roy van Rijn

01 Dec 2008 | TheServerSide.com

Introduction

All modern Java web applications use Servlets and Filters. They are the backbone of Java EE, the communication gateway to the World Wide Web.

Now there is a new specification coming, Servlet 3.0 (JSR-315). The Early Draft of this specification features some new really neat features, and in my opinion some mistakes. In this article I'm going to show the new additions to the EOD (ease of development), comment on them, and try to improve them.

Servlets

Since Servlet 2.3 we've been using the Servlet interface to create our Servlets. This interface has one main method called service(ServletRequest reand a ServletResponse. The abstract class HttpServlet helps us to create a HTTP suitable Servlet. It contains methods like doGet and doPost.

The easiest way to create custom behavior was to extend the HttpServlet and implement your own doGet and/or doPost.

public class GetPostServlet extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
     ....
  }
}

To instruct a Servlet Container to use your Servlet you have to add them to a file called the web.xml. This file contains mapping information from URL to Servlet, so when a user requests the mapped URL the Servlet will be called and asked for a response.

JSR-315

Servlet 3.0 has a couple of proposed changes. One of the big changes is adding support for continuations, in the new API you can call request.suspend() to suspend the request, and later (from another thread) call request.resume(), the Servlet is then again called to resume the request.

The second important addition is pluggability. Servlets can now be added to the container during runtime. If you create an instance of the ServletContextListener you can call servletContext.addServlet() and servletContext.addServletMapping() to add Servlets on runtime. Of course this poses a security risk, because included JARs could register their own Servlets, something you definitely don't want. But the specification is aware of this, and working on making it safer (for example, giving you a switch to turn auto-detection off).

The third major addition are the new annotations. Here is an example of these annotations in use:

@Servlet(urlMapping={"/myServlet"}, name="MyServlet")
public class PojoServlet() {
  @GET
  public void handleGet(HttpServletRequest req, HttpServletResponse resp) {
     ....
  }
}

As you can see there are a couple of new features. First of all, the Servlet is a POJO; there is no more interface/abstract class. Also notice the url mapping in the @Servlet annotation; no need for the mapping in the web.xml.

Then there are the Filters:

@ServletFilter
@FilterMapping(urlPatter="/myFilter")
public class PojoFilter() {
  public void doFilter(HttpServletRequest req, HttpServletResponse resp) {
     ....
  }
}

Here we can see a ServletFilter with the new annotations. This again has the mapping in the annotation, no need for the web.xml and it is a POJO again, no more interface/abstract class.

What is the problem?

So you might ask, what is wrong with this proposals new ease of development?

Well, maybe a couple of things. First of all, lets see what these new annotations add:

  • The mapping information is in the Servlet
  • The Servlets are POJO's
  • You can specify which method handles the GET and POST

Well, first of all the mapping information, which is a great feature. Although the naming of the annotations is a bit unfortunate. Why does the @Servlet annotation have the mapping inside, and does the @ServletFilter have a seperate annotation @FilterMapping? It would be much cleaner to have one way of declaring these mappings.

The second and third change (the ability to specify the GET and POST with @GET, @POST, @HEAD, @PUT etc.) has only one small advantage. Who hasn't written the following code while creating Servlets:

public class GetPostServlet extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    handleRequest(req,res);
  }
  public void doPost(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    handleRequest(req,res);
  }

  /* Handle GET and POST */
  public void handleRequest(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    ....
  }
}

This could be done a lot easier with the new annotations:

@Servlet
public class GetPostServlet {
  @GET
  @POST
  public void handleRequest(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    ....
  }
}

So, nothing wrong here! Well, I asked if you could specify two of these annotation on one method during the launch of the specification on JavaOne 2008, but the spec-lead wasn't sure this could be done. So this is probably not the way its meant to be used.

And there is more, this also adds problems to the code, for example what if I coded this:

@Servlet
public class GetPostServlet {
  @GET
  public void requestOne(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    ....
  }
  @GET
  public void requestTwo(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
    ....
  }
}

Now which method will be called? I have no idea! This will probably not even be documented, just like having two Servlets mapped on the same URL, you just can't predict what will happen, its up to the container.

These annotations are (supposed to be) added to help the developers, but there are more problems. The IDE doesn't know you are making a Servlet just because you add one annotation, so there is no support from the current generation of IDE's. When we had the interface or abstract class you would just override the methods you want, and you have code-completion. This is a huge advantage we'll loose when programming with these annotations!

It gets even worse if you look at the @ServletFilter. When you add this annotation to your code it turns the POJO into a Filter. But there is no check if you have implemented the doFilter() method. Also, it is much more error-prone, what if you accidently misspell "doFilter"?

The same thing also applies to the @ServletListenerContext, just look at this example:

@ServletContextListener
public class MyListener {
  public void contextInitialized(ServletContextEvent sce) {
     ....
  }
}

Again, nothing will check if you correctly spelled "contextInitialized" (which is a pretty hard word to type over and over without error-checking), you'll always have to manually double check. I'd rather type "implements ServletContextListener" instead of "@ServletContextListener" and then let my IDE write the implementing methods.

Okay, that is enough tough feedback. Next I would like to do something constructive and explain what direction I would like to see Servlet 3.0 go.

Proposal 1

The idea of having annotations for the url-mapping if very good. URLs are a good example of metadata, it doesn't say anything about the class or inner workings, but it does tell the container when to call it. This is something you would use an annotation for, also because its not mandatory to have mapping in the class, it would work perfectly without it. But as I said before, the naming is a bit off, I'd rather see this:

@ServletMapping(name="....", mapping={"...","..."}) //Use for both Servlets and Filters

The second thing is the annotations (@GET, @POST). I don't think this will help the user in any way, so just leave them out. I know the standard reply will be "Just don't use them if you don't like them...".

But there are already a lot of Java features we should just 'not use'. The problem is, people WILL use them, and it doesn't help them! In fact it will only add more choices/options for the programmers, making it all less clear if you incidentally have to work with Servlets. I personally haven't heard anybody complain about the interfaces/abstract classes before... And I can't seem to find any new advantage that it adds, only loosing the type-safety!

This was exactly the thing I was afraid of when they introduced annotations, people who start using them because they can, not because they have some metadata they want to have near the classes instead of in separate XML files.

Proposal 2

But *sigh* if they really want to use the annotations on the methods, then please add functionality to the Servlets. Currently they'll just serve as a (bad) alternative way of writing Servlets. But with a slight change you could actually add new features with these annotations, like this:

public class ExampleServlet {
  
  @ServletMethod(name="LoginServlet", mapping="/login", type={ServletType.GET, ServletType.POST})
  public void handleLogin(...) { ... }
  
  //And:
  
  @FilterMethod(name="AuthorizationFilter", mapping="/login", type={ServletType.POST})
  public void doAuthorization(...) { ... }
}

This way you can have one class (POJO) with all the handling methods for a specific Use Case. More or less like the new Spring MVC Controllers. You'll still have the same functionality as the proposed JSR-315 annotations, but now you can group Servlet methods in one class! You still have the drawbacks I mentioned before, regarding the type-safety, but by using these annotations you actually gain some functionality and freedom.

Conclusion

My preference would still be not using annotations at all for GET/POST methods, but I've read the Java EE 6 specification is promoting the use of annotations like this and the JSR-315 writers have 'no choice' (bad excuse). The comments I made in this article have been send to the JSR-group, but I haven't got a response yet.

Also I've been unable to reach members for a reaction and/or explanation and/or clarification. Recently the Java EE 6 specification went on public review, and it contains references to this Servlet 3.0 specification, so it will become part of Java EE 6. This must mean they are working actively, maybe even franticly, to finish it on time. But I plead them to take the time to reconsider their choices about the annotations.

Related Topics: Java servlets, VIEW ALL TOPICS