Introduction
This article covers most of the important features available as part of Servlet 3.0 specification. Note that the Servlet 3.0 specification constantly keeps changing frequently with the reviews coming in and the features and the API’s mentioned in this article is based on the specification that is available in JCP for public review as of December 2008. This article focuses on the new set of annotations introduced that can be used by developers rather than put the data in the configuration file, followed by the enhanced Pluggability and the extension support for adding third-party frameworks. The article is finally concluded by detailing about the asynchronous execution of processing and the usage for the same. JSR 315 talks about the Servlet 3.0 features. For attitional information please read the reference section of this article.
also read:
Ease of Development through Annotations
The configuration information about a component in a typical web application is expressed in an external meta file. The information about web components such as Servlets, Servlet Filters are mentioned in the deployment descriptor, which is web.xml
. Starting from Servlet 3.0, it is also possible to specify the meta information about a component in the definition of a component itself, through Annotations. It doesn’t mean the deployment descriptor is now gone, deployment descriptor in the form of web.xml is still there. In fact information specified in the deployment descriptor takes precedence over the information specified through Annotations.
The Servlet 3.0 specification also provides an option for instructing the Web Container, whether the container should process the annotations defined on the web components. The name of the element is metadata-complete and it is a child element of web-app element. The metadata-complete element indicates whether the meta-data information available in the deployment descriptor is complete. So, if the value for the metadata-complete element is set to a value of true, then it means that the meta information found in the deployment descriptor is complete and eventually the annotations defined on the web components will be ignored by the Servlet Container. If the value for metadata-complete is set to false, then it means that the information in the deployment descriptor is not complete and web components decorated with annotations, if any, should be scanned and processed by the Web Container.
The following annotations are applicable starting from Servlet 3.0 specification,
- @WebServlet
- @WebServletContextListener
- @ServletFilter
- @InitParam
Web Servlet and InitParam Annotation
In this section, we will see the usage of @WebServlet and @InitParam using an example. Look at the following code,
SimpleServlet.java
package net.javabeat.servlet30.newfeatures; import javax.servlet.annotation.InitParam; import javax.servlet.annotation.WebServlet; @WebServlet( name = "SimpleServlet", urlPatterns = {"/simple"}, initParams = { @InitParam(name = "param1", value = "value1"), @InitParam(name = "param2", value = "value2")} ) public class SimpleServlet { }
In the example, we have declared a class by name SimpleServlet and this class is not made to extend or implement any of the Servlet/HttpServlet types. Instead, to qualify this class as a Servlet class we have annotated using @WebServlet annotation. Note that the name of the servlet is SimpleServlet as specified through the name attribute. The attribute urlPatterns defines a set of url-patterns that can be used to invoke the Servlet. The Servlet Container after scanning this class will generate the deployment descriptor which may look like the following,
web.xml
... <servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class>net.javabeat.servlet30.newfeatures.SimpleServlet</servlet-class> <init-param> <param-name>param1</param-name> <param-value>value1</param-value> </init-param> <init-param> <param-name>param2</param-name> <param-value>value2</param-value> </init-param> </servlet> <servlet-mapping> <url-pattern>/simple</url-pattern> <servlet-name>SimpleServlet</servlet-name> </servlet-mapping> ...
Filter annotation
The @Filter annotation defines a Servlet Filter component for a web application. A filter is typically used to intercept a web request for performing any of the pre-processing operations well before reaching the actual servlet component. Let us see the definition of a filter component using Servlet 3.0 approach,
SimpleFilter.java
package net.javabeat.servlet30.newfeatures; import javax.servlet.annotation.InitParam; import javax.servlet.annotation.ServletFilter; @ServletFilter( filterName = "SimpleFilter", urlPatterns = "/simple", initParams = {@InitParam(name = "param1", value = "value1"), @InitParam(name = "param2", value = "value2")} ) public class SimpleFilter { }
Again, to support backward compatibility, the annotation information in the above class will be transformed as information in the deployment descriptor by the Servlet Container and the deployment descriptor might look like this,
web.xml
... <filter> <filter-name>SimpleFilter</filter-name> <filter-class>net.javabeat.servlet30.newfeatures.SimpleFilter</filter-class> <init-param> <param-name>param1</param-name> <param-value>value1</param-value> </init-param> <init-param> <param-name>param2</param-name> <param-value>value2</param-value> </init-param> </filter> <filter-mapping> <filter-name>SimpleFilter</filter-name> <url-pattern>/simple</url-pattern> <filter-mapping> ...
Servlet Context Listener annotation
The Servlet Context Listener is used to receive events whenever the servlet context is created and destroyed by the Web Container. Let us see an usage of this annotation,
SimpleServletContextListener.java
package net.javabeat.servlet30.newfeatures; import javax.servlet.ServletContextEvent; import javax.servlet.annotation.WebServletContextListener; @WebServletContextListener() public class SimpleServletContextListener { public void contextInitialized(ServletContextEvent event){ } public void contextDestroyed(ServletContextEvent event){ } }
The deployment descriptor in this case would be,
web.xml
<web-app> <listener> <listener-class>net.javabeat.servlet30.newfeatures.SimpleServletContextListener </listener-class> </listener> </web-app>
Pluggability in Servlet 3.0
Web Fragments
Today, most of the modern popular frameworks such as Struts and Spring can be integrated easily with the Web Container for building robust applications. However, most of the time, the integration happens in such a way that the web.xml deployment descriptor has to be edited manually to configure framework specific servlet and listener classes. Have a look into the following code snippet,
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> </web-app>
The above web.xml will be familiar for someone who had used the JSF framework. We have used a framework-specific servlet called the FacesServlet in the above case for intercepting all the url requests with the pattern /faces/*. Same will be the case for another intergration frameworks such as Spring or Struts.
The problem with the current approach of the web.xml is that it is not modular. All the configuration details about one particular application is included in one single web.xml file. Assuming that if one single application is making use of one or more frameworks on top of it, there is no way to instruct the Servlet Container about the usage of other frameworks, the only option being is to edit the web.xml deployment descriptor. The Servlet 3.0 specification addresses this issue by introducing web fragments.
A web fragment can be considered as one of the segment of the whole web.xml and it can be imagined that one or more web fragments constitute a single web.xml file. A web-fragment can include all the possible elements that are applicable for the web.xml. Consider a sample web-fragment file,
web-fragment.xml
<web-fragment> <servlet> <servlet-name>myFrameworkSpecificServlet</servlet-name> <servlet-class>myFramework.myFrameworkServlet </servlet-class> </servlet> <listener> <listener-class>myFramework.myFrameworkListener</listener-class> </listener> </web-fragment>
Typically a framework is bundled in the form of a jar file and it is the responsibility of that framework to define the web fragment file with the name web-fragment.xml in the META-INF directory of the jar file. During the application startup, it is the responsibility of the Container to scan the information that is found in the /META-INF/web-fragment.xml file available in the application’s classpath.
Programmatic support for adding web components
The specification also provides enhanced Pluggability where it provides options for adding servlets, filters, servlet mappings and filter mappings during the run time. Look into the following example code,
SimpleServletContextListener.java
package net.javabeat.servlet30.newfeatures; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.annotation.WebServletContextListener; @WebServletContextListener() public class SimpleServletContextListener { public void contextInitialized(ServletContextEvent event){ ServletContext context = event.getServletContext(); String simpleServletName = "simpleServlet"; String simpleFilterName = "simpleFilter"; Map<String, String> initParams = new HashMap<String, String>(); initParams.put("param1", "value1"); initParams.put("param2", "value2"); String[] urlPatterns = new String[]{"/simple"}; context.addServlet(simpleServletName, "description for simple servlet", "net.javabeat.servlet30.newfeatures.SimpleServlet", initParams, -1, false); context.addServletMapping(simpleServletName, urlPatterns); context.addFilter(simpleFilterName, "description for simple filter", "net.javabeat.servlet30.newfeatures.SimpleFilter", initParams, false); } public void contextDestroyed(ServletContextEvent event){ } }
The code is pretty straightforward. It acquires the reference to ServletContext and makes use of the methods addServlet(), addServletMapping() and addFilter() for dynamically adding web components.
Asynchronous processing in Servlet 3.0
In a web application workflow, the client that initiates the request will be received by the Web Container and it is the responsibility of the Web Container to initiate the Servlet object by passing in the request and the response objects by calling in any of the request objects. Assume that the implementation code that tries to access external system, example a Database or a legacy system using JDBC or the JCA API’s is taking considerable amount of time for the execution to happen. In a normal environment the thread will get blocked and it will be made to wait until the data from the external system is available.
To avoid this waiting time or the block time, Servlet 3.0 adds support for asynchronous processing of request. The request can be made to put into asynchronous mode by calling the method ServletRequest.startAsync() that returns a AsyncContext object. It is also possible to specify the timeout duration by calling the ServletRequest.setAsyncTimeout() method. In this case, the response can be committed via two means, one is to call the complete() method that is defined on the AsyncContext object and the other way is when the timeout duration that is originally set on the request object has expired.
It is also possible to add asynchronous listeners to the request object to receive notifications whether the asynchronous operation is completed or the timeout has happened. The following code does the same,
SimpleAsyncListener.java
package net.javabeat.servlet30.newfeatures; import java.io.IOException; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletRequest; public class SimpleAsyncListener implements AsyncListener{ public void onComplete(AsyncEvent event) throws IOException { } public void onTimeout(AsyncEvent event) throws IOException { } }
To add this listener to the request object, use the following piece of code,
request.addAsyncListener(new SimpleAsyncListener());
The same is reflected in the Web Servlet and the Servlet Filter annotations through the asyncSupported
and asyncTimeout
attributes,
SimpleServlet.java
package net.javabeat.servlet30.newfeatures; import javax.servlet.annotation.InitParam; import javax.servlet.annotation.WebServlet; @WebServlet( name = "SimpleServlet", urlPatterns = {"/simple"}, initParams = { @InitParam(name = "param1", value = "value1"), @InitParam(name = "param2", value = "value2")}, asyncSupported = true, asyncTimeout = 3000 ) public class SimpleServlet { }
Conclusion
The article covered the new set of annotations like @WebServlet, @ServletFilter that can be used to directly decorate the web components: Servlet and Filter, which could have done previously only through the usage of deployment descriptors. However, it should be noted that the usage of annotations is intended still for developers and not for the others who typically play the role of assembler or deployer. The Servlet 3.0 adds extensible support for plugging-in new frameworks or libraries without asking the developers to edit the deployment descriptor through web fragments. Finally the asynchronous nature of request processing, the possibility of adding asynchronous listeners along with the impacts in @WebServlet and @ServletFilter annotations were also discussed. Thank you for reading this article.