In the past whenever you wrote a Servlet you had a lot of work to do. First you wrote your servlet, then you had to add configuration for that servlet into web.xml so that your application would use it.
For simple applications this was fine but the more servlets you wrote the harder it became as web.xml started to become large and unwieldy.
Now with J2EE 6 you got the ability to add annotations to your servlets. No more do you need to add anything to web.xml as the container scans the classpath for any servlet that’s annotated with @WebServlet
getting the required configuration from there.
This also has the benefit that you can write libraries of servlets. Those servlets get deployed automatically and you don’t have to worry about duplicating the config.
Both Tomcat 7 and TomEE 1.5.1 support the J2EE 6 Web Profile as standard. I’m not certain of other containers as I’ve only tested this on those two.
Writing a Servlet the old way
First you wrote a Servlet:
package org.myapp; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet { @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { processRequest( request, response ); } @Override protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { processRequest( request, response ); } protected void processRequest( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.getWriter().println( "Hello World!" ); } }
Then you added the config into web.xml:
<servlet> <servlet-name>My Servlet</servlet-name> <servlet-class>org.myapp.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/myservlet/*</url-pattern> </servlet-mapping>
Now imaging doing that dozens of times… yes it becomes unmanageable. It’s because of this for the rise of other web frameworks. Well not the sole reason but one thing other frameworks try to do is to make it simpler and more maintainable – this way it isn’t.
Writing a Servlet the new way
In J2EE 6 it’s now simpler. All you need to do is to annotate the Servlet itself with the @WebServlet
annotation:
package org.myapp; 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( "name="MyServlet", urlPatterns = "/myservlet/*" ) public class MyServlet extends HttpServlet { @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { processRequest( request, response ); } @Override protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { processRequest( request, response ); } protected void processRequest( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.getWriter().println( "Hello World!" ); } }
Thats it. Nothing goes into web.xml. When the container deploys the application it sees the @WebServlet annotation and uses that configuration to deploy the servlet.
What about servlets with multiple url mappings you may ask?
Well the urlPatterns property in the annotation is actually an array. In the example above I’ve used shorthand where javac will allow a single value to be written without an array but it’s possible to add multiple patterns easily:
@WebServlet( "name="MyServlet", urlPatterns = { "/myservlet/*", "/myotherservlet" } )
Other parameters
The annotation includes additional parameters which map to the elements you would have put into web.xml originally.
boolean |
asyncSupported Declares whether the servlet supports asynchronous operation mode. |
java.lang.String |
description The description of the servlet |
java.lang.String |
displayName The display name of the servlet |
WebInitParam[] |
initParams The init parameters of the servlet |
java.lang.String |
largeIcon The large-icon of the servlet |
int |
loadOnStartup The load-on-startup order of the servlet |
java.lang.String |
name The name of the servlet |
java.lang.String |
smallIcon The small-icon of the servlet |
java.lang.String[] |
urlPatterns The URL patterns of the servlet |
java.lang.String[] |
value The URL patterns of the servlet |
The more useful ones here are:
loadOnStartup
– set this to 1 and your servlet is loaded when the webapp is. Leave this out and the servlet gets loaded when it’s first requested.initParams
– this is an array of @WebInitParam annotations containing the servlet’s initial parameters.
Although it might seem strange to have initParameters in an annotation when you could do it in code but it is beneficial in that:
- You keep the configuration in a single place
- Your Servlet might inherit from another one. Placing configuration on the deployed servlet can then be passed on to the super class.
ServletContextListener’s
ServletContextListener
‘s can also be annotated with the @WebListener
annotation. This is useful if you are deploying a framework which must perform actions when the application is deployed or destroyed.
In the past you had to declare each one in web.xml with something like the following:
<listener> <listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class> </listener>
Now you can simply use:
package org.myapp; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class MyServletContextListener implements ServletContextListener { public void contextInitialised( ServletContextEvent sce ) { // do something here } public void contextDestroyed( ServletContextEvent sce ) { // do something here } }
The really useful thing here is that no more would you forget to declare the listener if you included your framework in a new application.
If your framework needs to cleanup on shutdown for example, now it will automatically get called whenever it’s in the classpath of your application.
Filter’s
Filters can also be declared with the @WebFilter annotation.
package org.myapp; import javax.servlet.Filter; import javax.servlet.annotation.WebFilter; @WebFilter( filterName = "MyFilter", urlPatterns = "/*" ) public class MyFilter implements Filter { // implementation goes here }
The stub above declares a filter which runs against all requests to the application. Like servlets urlPatterns is an array of url patterns although you can also use servletNames instead which declares the servlets this filter is to run against.
The only downside I can see with this is that you cannot determine the order that filters run – in web.xml they run in the order they are declared. This can be a problem for some applications but for most this is enough.
That’s all for now about Servlet 3.0. There’s a couple of topics not covered, like security & multipart upload support but those will be covered by separate articles.
Great migration cookbook, thanks a lot.
You mentioned security: Do you have experience with servlet 3.0 and form-login? I didn’t find any examples in the whole wide web but in a tutorial I read something that this would not be possible to do with annotations, when using form login a deployment descriptor is mandatory.
Did you ever migrate a secure servlet to 3.0?
Stephan
I did manage a form login, I just had a form which went to a settler and then used the new login methods, passing authentication to the container – tomcat in this case.
It worked pretty well. Even got Tomcat’s SSO to work so the auth module was a separate webapp. In that instance once authenticated it then redirected back to the original resource.
I have to say servlet 3.0 is way better than the earlier api’s.
is it possible to map the url to a method ? a bit like @RequestMapping in spring ?
@WebServlet(“/my”)
class MyServlet extends HttpServlet {
/** handle create */
@XAnnotation(value = “/create”)
public String create() {
}
}