Google

Jun 9, 2014

When to use a ServletContextListener in Java EE? Understanding ServletContext and ServletContextListener with examples

A ServletContext object refers to the whole web application, whilst a ServletConfig is for a Servlet. Once an application is deployed onto the web application, ServletContextListener may be configured using annotations or the configuration file named web.xml. A ServletContext object is initialized once while a web application is being deployed, and this object can be accessed from anywhere within the same web application.

Q. What are servlet lifecycle events?
A. Servlet lifecycle events work like the Swing events. Any listener interested in observing the ServletContext lifecycle can implement the ServletContextListener interface and in the ServletContext attribute lifecycle can implement the ServletContextAttributesListener interface. The session listener model is similar to the ServletContext listener model (Refer Servlet spec 2.3 or later). Servlet contexts’ and and sessions’ listener objects are notified when servlet contexts and sessions are initialized and destroyed, as well as when attributes are added or removed from a context or session.

Example 1: You can declare a listener in the web.xml deployment descriptor as follows:

<listener>
      <listener-class>com.MyJDBCConnectionManager </listener-class>
</listener>


You can create a custom listener class as shown below:

public class MyJDBCConnectionManager implements ServletContextListener {

  public void contextInitialized(ServletContextEvent event) {
       Connection con  = // create connection
       event.getServletContext().setAttribute("con", con);
  }

  public void contextDestroyed(ServletContextEvent e) {
       Connection con = (Connection) e.getServletContext().getAttribute("con");
       try { con.close(); } catch (SQLException ignored) { } // close connection
  }
}


Example 2: bootstrapping spring into RESTEasy   framework

A typical example where a ServletContextListener is used is in bootstarpping a spring applicationContet.xml file into a web application that is using RESTEasy for RESTful web services. For example, if under com.mytutorial  you are having an applicationContet.xml, which defines the beans for web service, POJO service, and dao layers.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
     <bean id="simpleRestWeb" class="com.mytutorial.SimpleRestWebImpl"  />
    
    .....
</beans>


The above spring context file needs to be bootstrapped via web.xml file when the web app is being deployed. This is accomplished via org.jboss.resteasy.plugins.spring.SpringContextLoaderListener for JBoss RESTEasy framework. This SpringContextLoaderListener is nothing but an implementation of ServletContextListener. In the web.xml file you will be defining

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>


 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:/com/mytutorial/applicationContext.xml</param-value>
 </context-param>

 <listener>
  <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
 </listener>
 <listener>
  <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
 </listener>
 
 
 <servlet>
  <servlet-name>resteasy-simple</servlet-name>
  <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
 </servlet>
 
 <servlet-mapping>
  <servlet-name>resteasy-simple</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>
 
</web-app>


Take note of the "contextConfigLocation" param definition where the spring context location is defined, and SpringContextLoaderListener is used to bootstrap this during deployment of the war file. Similar ServletContextListener based approach is used by other web frameworks for bootstrapping.


Example 3copying a bean from spring context into servlet context for Yammer metrics

Another scenario that I came across recently where I had to provide my own implementation of the ServletContextListener when using yammer metrics to provide web based monitoring. If you register yammer via your spring applicationContext.xml file as shown below

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:metrics="http://www.yammer.com/schema/metrics"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                        http://www.yammer.com/schema/metrics http://www.yammer.com/schema/metrics/metrics.xsd">
    
    <metrics:metrics-registry id="rest-metrics"/>
    <metrics:health-check-registry id="rest-health"/>
    <metrics:annotation-driven metrics-registry="rest-metrics" health-check-registry="rest-health" proxy-target-class="true" />
    
    <bean id="simpleRestWeb" class="com.mytutorial.SimpleRestWebImpl"  />
    
</beans>


Now, I needed to get the MetricRegistry out if the Spring context and then put it on to the Servlet context so that the admin servlet that comes with Yammer can find it to report metrics via web URL like http://localhost:8080/tutorial/admin. Here is an implementation of ServletContextListener.

package com.mytutorial;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.reporting.MetricsServlet;

/**
 * Application Lifecycle Listener for Metrics.
 * 
 *I need to fetch the MetricsRegistry from the Spring Context and put it into the ServletContext,
 * for the yammer admin-servlet to be able to find it.....
 *
 */
public class MetricsContextLoaderListener implements ServletContextListener {

    public MetricsContextLoaderListener() {

    }

 public void contextInitialized(ServletContextEvent event) {
        ServletContext context = event.getServletContext();
        MetricsRegistry metricsRegistry = getMetricsRegistry(context);
        setMetricsRegistry(metricsRegistry, context);
    }

 
    public void contextDestroyed(ServletContextEvent event) {

    }

   
    protected MetricsRegistry getMetricsRegistry(ServletContext context) {
        WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(context); 
        return springContext.getBean(MetricsRegistry.class);
    }
    
   
    protected void setMetricsRegistry(MetricsRegistry registry, ServletContext context) {
        context.setAttribute(MetricsServlet.REGISTRY_ATTRIBUTE, registry);
    }
}


You can now define this listener and Yammer metrics servlet via the web.xml file

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>


 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:/com/mytutorial/applicationContext.xml</param-value>
 </context-param>

 <context-param>
  <param-name>resteasy.servlet.mapping.prefix</param-name>
  <param-value>/rest</param-value>
 </context-param>


 <listener>
  <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
 </listener>
 <listener>
  <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
 </listener>
  <listener>
   <listener-class>com.mytutorial.MetricsContextLoaderListener</listener-class>
  </listener>

  <servlet>
  <servlet-name>resteasy-simple</servlet-name>
  <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
 </servlet>
 <servlet>
  <servlet-name>admin-servlet</servlet-name>
  <servlet-class>com.yammer.metrics.reporting.AdminServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>


 <servlet-mapping>
  <servlet-name>resteasy-simple</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>admin-servlet</servlet-name>
  <url-pattern>/admin/*</url-pattern>
 </servlet-mapping>
</web-app>


More on Servlets

Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home