WEBSITE

Java EE 6 : New features introduced in Servlet 3.0 (part 2)

8/24/2012 3:20:53 AM
@WebListener annotation

During the lifetime of a typical web application, a number of events take place, such as HTTP requests are created or destroyed, request or session attributes are added, removed, or modified, and so on and so forth.

The Servlet API provides a number of listener interfaces we can implement in order to react to these events. All of these interfaces are in the javax.servlet package. The following table summarizes them:

Listener Interface Description
ServletContextListener Contains methods for handling context initialization and destruction events.
ServletContextAttributeListener Contains methods for reacting to any attributes added, removed, or replaced in the servlet context (application scope).
ServletRequestListener Contains methods for handling request initialization and destruction events.
ServletRequestAttributeListener Contains methods for reacting to any attributes added, removed, or replaced in the request.
HttpSessionListener Contains methods for handling HTTP session initialization and destruction events.
HttpSessionAttributeListener Contains methods for reacting to any attributes added, removed, or replaced in the HTTP session.

All we need to do to handle any of the events handled by the interfaces described in this table is to implement one of these interfaces and annotate it with the @WebListener interface or declare it in the web.xml deployment descriptor via the<listener> tag. Unsurprisingly, the ability to use an annotation to register a listener was introduced in version 3.0 of the Servlet specification.

The API for all of these interfaces is fairly straightforward and intuitive. We will show an example for one of these interfaces; others will be very similar.


The following code example illustrates how to implement the ServletRequestListener interface, which can be used to perform some action whenever an HTTP request is created or destroyed:

package net.ensode.glassfishbook.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
@WebListener()
public class HttpRequestListener implements ServletRequestListener
{
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent)
{
ServletContext servletContext = servletRequestEvent.getServletContext();
servletContext.log("New request initialized");
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent)
{
ServletContext servletContext =
servletRequestEvent.getServletContext();
servletContext.log("Request destroyed");
}
}


					  

As we can see, all we need to do to activate our listener class is to annotate it with the @WebListener annotation. Our listener must also implement one of the listener interfaces we listed previously. In our example, we chose to implement javax.servlet.ServletRequestListener. This interface has methods that are automatically invoked whenever an HTTP request is initialized or destroyed.

The ServletRequestListener interface has two methods: requestInitialized() and requestDestroyed(). In our previous simple implementation, we simply sent some output to the log, but of course we can do anything we need to do in our implementations.

We can see the following output in GlassFish's log:

[#|2009-10-03T10:37:53.465-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=39;
_ThreadName=Thread-2;|PWC1412: WebModule[/nbservlet30listener] ServletContext.log():New request initialized|#] [#|2009-10-03T10:37:53.517-0400|INFO|glassfish|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=39;
_ThreadName=Thread-2;|PWC1412: WebModule[/nbservlet30listener] ServletContext.log():Request destroyed|#]

Implementing the other listener interfaces is just as simple and straightforward.

Pluggability

When the original Servlet API was released back in the late 1990s, writing servlets was the only way of writing server-side web applications in Java. Since then, several standard Java EE and third-party frameworks have been built on top of the Servlet API. Examples of such standard frameworks include JSP's and JSF, third-party frameworks include Struts, Wicket, Spring Web MVC, and several others.

Nowadays, very few (if any) Java web applications are built using the Servlet API directly. Instead, the vast majority of projects utilize one of the several available Java web application frameworks. All of these frameworks use the Servlet API "under the covers". Therefore, setting up an application to use one of these frameworks has always involved making some configuration in the application's web.xml deployment descriptor. In some cases, some applications use more than one framework. This tends to make the web.xml deployment descriptor fairly large and hard to maintain.

Servlet 3.0 introduces the concept of pluggability. Web application framework developers now have not one, but two ways to avoid having application developers modify the web.xml deployment descriptor in order to use their framework. Framework developers can choose to use annotations instead of a web.xml to configure their servlets. After doing this, all that is needed to use the framework is to include the library JAR file(s) provided by the framework developers in the application's WAR file. Alternatively, framework developers may choose to include a web-fragment.xml file as part of the JAR file to be included in web applications that use their framework.

web-fragment.xml is almost identical to web.xml. The main difference is that the root element of a web-fragment.xml file is<web-fragment> as opposed to<web-app>. The following code example illustrates a sample web-fragment.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment version="3.0" 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-fragment_3_0.xsd"> <servlet> <servlet-name>WebFragment</servlet-name> <servlet-class> net.ensode.glassfishbook.webfragment.WebFragmentServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>WebFragment</servlet-name> <url-pattern>/WebFragment</url-pattern> </servlet-mapping> </web-fragment>

As we can see, web-fragment.xml is almost identical to a typical web.xml. In this simple example we only use the<servlet> and<servlet-mapping> elements. However, all other usual web.xml elements such as<filter>, <filter-mapping>, and<listener> are available as well.

As specified in our web-fragment.xml file, our servlet can be invoked via its URL pattern, /WebFragment. Therefore, the URL to execute our servlet, once deployed as part of a web application, would be http://localhost:8080/webfragmentapp/WebFragment. Of course, the host name, port, and context root must be adjusted as appropriate.

All we need to do for GlassFish or any Java EE 6-compliant application server to pick up the settings in web-fragment.xml is to place the file in the META-INF folder of the library where we pack our servlet, filter, and/or listener, then place our library's JAR file in the lib directory of the WAR file containing our application.

Configuring web applications programmatically

In addition to allowing us to configure web applications through annotations and through a web-fragment.xml file, Servlet 3.0 also allows us to configure our web applications programmatically at runtime.

The ServletContext class has new methods to configure servlets, filters, and listeners programmatically. The following example illustrates how to configure a servlet programmatically at runtime, without resorting to the @WebServlet annotation or to XML:

package net.ensode.glassfishbook.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;
@WebListener()
public class ServletContextListenerImpl implements ServletContextListener
{
@Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
ServletContext servletContext = servletContextEvent.getServletContext();
try
{
ProgrammaticallyConfiguredServlet servlet = servletContext. createServlet(ProgrammaticallyConfiguredServlet.class);
servletContext.addServlet("ProgrammaticallyConfiguredServlet", servlet);
ServletRegistration servletRegistration = servletContext.getServletRegistration( "ProgrammaticallyConfiguredServlet");
servletRegistration.addMapping( "/ProgrammaticallyConfiguredServlet");
}
catch (ServletException servletException)
{
servletContext.log(servletException.getMessage());
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent)
{
}
}


					  
In this example, we invoke the createServlet() method of ServletContext to create the servlet that we are about to configure. This method takes an instance of java.lang.Class corresponding to our servlet's class. This method returns a class implementing javax.servlet.Servlet or any of its child interfaces (thanks to Generics, a Java language feature introduced in Java 5, we don't need to explicitly cast the return value to the actual type of our servlet).

Once we create our servlet, we need to invoke addServlet() on our ServletContext instance to register our servlet with the servlet container. This method takes two parameters: the first being a String corresponding to the servlet name, the second being the servlet instance returned by a call to createServlet().

Once we have registered our servlet, we need to add a URL mapping to it. In order to do this, we need to invoke the getServletRegistration() method on our ServletContext instance, passing the servlet name as a parameter. This method returns the servlet container's implementation of javax.servlet.ServletRegistration. From this object, we need to invoke its addMapping() method, passing the URL mapping we wish our servlet to handle.

Our example servlet is very simple. It simply displays a text message on the browser.

package net.ensode.glassfishbook.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ProgrammaticallyConfiguredServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ServletOutputStream outputStream = response.getOutputStream();
outputStream.println("This message was generated from a servlet that was " + "configured programmatically.");
}
}


					  
After packing our code in a WAR file, deploying to GlassFish and pointing the browser to the appropriate URL (http://localhost:8080/programmaticservletwebapp/ProgrammaticallyConfiguredServlet, assuming we packaged the application in a WAR file named programmaticservletwebapp.war and didn't override the default context root), we should see the following message in the browser:

This message was generated from a servlet that was configured programmatically.

The ServletContext interface has methods to create and add servlet filters and listeners. They work very similarly to the way the addServlet() and createServlet() methods work, therefore we won't be discussing them in detail. Refer to the Java EE 6 API documentation at http://java.sun.com/javaee/6/docs/api/ for details.

Asynchronous processing

Traditionally, servlets have created a single thread per request in Java web applications. After a request is processed, the thread is made available for other requests to use. This model works fairly well for traditional web applications in which HTTP requests are relatively few and far in between. However, most modern web applications take advantage of Ajax (Asynchronous JavaScript and XML), a technique that makes web applications behave much more responsively than traditional web applications. Ajax has the side effect of generating a lot more HTTP requests than traditional web applications. If some of these threads block for a long time waiting for a resource to be ready or are doing anything that takes a long time to process, it is possible our application may suffer from thread starvation.

To alleviate the situation described in the previous paragraph, the Servlet 3.0 specification introduced asynchronous processing. Using this new capability, we are no longer limited to a single thread per request. We can now spawn a separate thread and return the original thread back to the pool to be reused by other clients.

The following example illustrates how to implement asynchronous processing using the new capabilities introduced in Servlet 3.0:

package net.ensode.glassfishbook.asynchronousservlet;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.AsyncContext;
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 = "AsynchronousServlet", urlPatterns = { "/AsynchronousServlet"}, asyncSupported = true)
public class AsynchronousServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
final Logger logger = Logger.getLogger(AsynchronousServlet.class.getName());
logger.log(Level.INFO, "--- Entering doGet()");
final AsyncContext ac = request.startAsync();
logger.log(Level.INFO, "---- invoking ac.start()");
ac.start(new Runnable()
{
@Override
public void run()
{
logger.log(Level.INFO, "inside thread");
try
{
//simulate a long running process.
Thread.sleep(10000);
}
catch (InterruptedException ex)
{
Logger.getLogger(AsynchronousServlet.class.getName()). log(Level.SEVERE, null, ex);
}
try
{
ac.getResponse().getWriter().println("You should see this after a brief wait");
ac.complete();
}
catch (IOException ex)
{
Logger.getLogger(AsynchronousServlet.class.getName()).
log(Level.SEVERE, null, ex);
}
}
});
logger.log(Level.INFO, "Leaving doGet()");
}
}


					  

The first thing we need to do to make sure our asynchronous processing code works as expected is to set the asyncSupported attribute of the @WebServlet annotation to true.

To actually spawn an asynchronous process, we need to invoke the startAsync() method on the instance of HttpServletRequest that we receive as a parameter in the doGet() or doPost() method in our servlet. This method returns an instance of javax.servlet.AsyncContext. This class has a start() method that takes an instance of a class implementing java.lang.Runnable as its sole parameter. In our example, we used an anonymous inner class to implement Runnable in line. Of course, a standard Java class implementing Runnable can be used as well.

When we invoke the start() method of AsyncContext, a new thread is spawned and the run() method of the Runnable instance is executed. This thread runs in the background, the doGet() method returns immediately, and the request thread is immediately available to service other clients. It is important to notice that even though the doGet() method returns immediately, the response is not committed until after the thread spawned finishes. It can signal it has finished processing by invoking the complete() method on AsyncContext.

In the previous example, we sent some entries to the GlassFish log file to illustrate better what is going on. Observing the GlassFish log right after our servlet executes, we should notice that all log entries are written to the log within a fraction of a second of each other. The message You should see this after a brief wait isn't shown in the browser until after the log entry indicating that we are leaving the doGet() method, gets written to the log.
Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8
Visit movie_stars's profile on Pinterest.