Java EE 6 includes a new
version of the Servlet API-Servlet 3.0. This version of the Servlet API
includes several new features that make servlet development easier.
Servlet 3.0 also makes it easier to take advantage of modern web
application techniques such as Ajax.
In the next several sections, we will discuss some of the most important additions to the Servlet API.
Optional web.xml deployment descriptor
Servlet 3.0 makes the web.xml deployment descriptor completely optional. Servlets can be configured via annotations instead of XML.
If a web application is configured both through annotations and through a web.xml deployment descriptor, settings in web.xml take precedence.
@WebServlet annotation
Servlets can be decorated with the @WebServlet
annotation to specify their name, URL pattern, initialization
parameters, and other configuration items usually specified in the web.xml deployment descriptor.
At a minimum, a servlet to be configured via annotations must have a @WebServlet annotation specifying the servlet's URL pattern.
package net.ensode.glassfishbook.simpleapp;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = {"/simpleservlet"})
public class SimpleServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
try
{
response.setContentType("text/html");
PrintWriter printWriter = response.getWriter();
printWriter.println("<h2>");
printWriter.println("If you are reading this, your application server " + "is good to go!");
printWriter.println("</h2>");
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
}
}
Notice that all we had to do was annotate our servlet with the @WebServlet annotation, and specify its URL pattern as the value of its urlPatterns attribute.
Just like with a web.xml,
we can specify more than one URL pattern for our servlet. All we need
to do is separate each URL pattern with a comma. For example, if we
wanted our servlet to handle all URLs ending with .foo in addition to handling all URLs starting with /simpleservlet, we would annotate it as follows:
@WebServlet(urlPatterns = {"/simpleservlet", "*.foo"})
With this simple change to our code, we avoid having to write a web.xml for our application.
After packaging and deploying this new version of the application, it will work just like the previous version.
Passing initialization parameters to a servlet via annotations
It is sometimes
useful to pass some initialization parameters to a servlet. This way, we
can make said servlet behave differently based on the parameters sent
to it. For example, we may want to configure a servlet to behave
differently in development and production environments.
Traditionally, servlet initialization parameters were sent via the<init-param> parameter in web.xml. As of servlet 3.0, initialization parameters can be passed to the servlet as the value of the initParams attribute of the @WebServlet annotation. The following example illustrates how to do this:
package net.ensode.glassfishbook.initparam;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "InitParamsServlet", urlPatterns = { "/InitParamsServlet"}, initParams = { @WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2")})
public class InitParamsServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ServletConfig servletConfig = getServletConfig();
String param1Val = servletConfig.getInitParameter("param1");
String param2Val = servletConfig.getInitParameter("param2");
response.setContentType("text/html");
PrintWriter printWriter = response.getWriter();
printWriter.println("<p>");
printWriter.println("Value of param1 is " + param1Val);
printWriter.println("</p>");
printWriter.println("<p>");
printWriter.println("Value of param2 is " + param2Val);
printWriter.println("</p>");
}
}
As we can see, the value of the initParams attribute of the @WebServlet annotation is an array of @WebInitParam annotations. Each @WebInitParam annotation has two attributes: name, which corresponds to the parameter name and value, which corresponds to the parameter value.
We can obtain the values of our parameters by invoking the getInitParameter() method on the javax.servlet.ServletConfig class. This method takes a single String argument as a parameter, corresponding to the parameter name, and returns a String corresponding to the parameter value.
Each servlet has a corresponding instance of ServletConfig assigned to it. As we can see in this example, we can obtain this instance by invoking getServletConfig(), which is a method inherited from javax.servlet.GenericServlet-the parent class of HttpServlet, which our servlets extend.
After packaging our
servlet in a WAR file and deploying it to GlassFish either via the
asadmin command line tool (the GlassFish web console) or by copying it
to the autodeploy directory in our domain, we will see the following page rendered in the browser:
As we can see, the rendered values correspond to the values we set in each @WebInitParam annotation.
@WebFilter annotation
Filters were
introduced to the servlet specification in version 2.3. A filter is an
object that can dynamically intercept a request and manipulate its data
before the request is handled by the servlet. Filters can also
manipulate a response after a servlet's doGet() or doPost() method finishes, but before the output is sent to the browser.
The only way to configure a filter in earlier servlet specifications was to use the<filter-mapping> tag in web.xml. Servlet 3.0 introduced the ability to configure servlets via the @WebFilter annotation.
The following code snippet illustrates how to do this:
package net.ensode.glassfishbook.simpleapp;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
@WebFilter(filterName = "SimpleFilter", initParams = {
@WebInitParam(name = "filterparam1", value = "filtervalue1")},
urlPatterns = {"/InitParamsServlet"})
public class SimpleFilter implements Filter
{
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
{
ServletContext servletContext = filterConfig.getServletContext();
servletContext.log("Entering doFilter()");
servletContext.log("initialization parameters: ");
Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
String parameterName;
String parameterValue;
while (initParameterNames.hasMoreElements())
{
parameterName = initParameterNames.nextElement();
parameterValue = filterConfig.getInitParameter(parameterName);
servletContext.log(parameterName + " = " + parameterValue);
}
servletContext.log("Invoking servlet...");
filterChain.doFilter(servletRequest, servletResponse);
servletContext.log("Back from servlet invocation");
}
@Override
public void destroy()
{
filterConfig = null;
}
}
As we can see in this code, the @WebFilter annotation has several attributes we can use to configure the filter. The urlPatterns attribute is of special importance. This attribute takes an array of String
objects as its value. Each element in the array corresponds to a URL
that our filter will intercept. In our example, we are intercepting a
single URL pattern that corresponds to the servlet we wrote in the
previous section.
Other attributes in the @WebFilter annotation include the optional filterName
attribute, which we can use to give our filter a name. If we don't
specify a name for our filter, then the filter name defaults to the
filter's class name.
As we can see in the previous
code example, we can send initialization parameters to a filter. This is
done just like we send initialization parameters to a servlet. The @WebFilter annotation has an initParams attribute that takes an array of @WebInitParam annotations as its value. We can obtain the values of said parameters by invoking the getInitParameter() method on javax.servlet.FilterConfig, as illustrated in the example.
Our filter is fairly
simple. It simply sends some output to the server log before and after
the servlet is invoked. Inspecting the server log after deploying our
application and pointing the browser to the servlet's URL should reveal
our filter's output.
[#|2009-09-30T19:38:15.454-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;
_ThreadName=Thread-1;|PWC1412: WebModule[/servlet30filter] ServletContext.log():Entering doFilter()|#]
[#|2009-09-30T19:38:15.456-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;
_ThreadName=Thread-1;|PWC1412: WebModule[/servlet30filter] ServletContext.log():initialization parameters: |#]
[#|2009-09-30T19:38:15.459-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;
_ThreadName=Thread-1;|PWC1412: WebModule[/servlet30filter] ServletContext.log():filterparam1 = filtervalue1|#]
[#|2009-09-30T19:38:15.461-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;
_ThreadName=Thread-1;|PWC1412: WebModule[/servlet30filter] ServletContext.log():Invoking servlet...|#]
[#|2009-09-30T19:38:15.471-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;
_ThreadName=Thread-1;|PWC1412: WebModule[/servlet30filter] ServletContext.log():Back from servlet invocation|#]
Of
course, servlet filters have many real uses. They can be used for
profiling web applications, for applying security, and for compressing
data, among many other uses.