Java Web Programming with Eclipse contents
Last modified February 05, 2011 04:42 pm

back next

Database Connection Pooling

Objectives

References

Video

Overview

In this chapter, we modify our approach to getting database connection objects. Rather than getting the connection object through the DriverManager class, we get them through an instance of the DataSource class. This is considered a best practice because a DataSource object maintains a pool of persistent database connections in order to eliminate the time consuming step of establishing a database connection each time the application wishes to interact with the database.

Configure the DataSource

There are two ways to configure a DataSource for use in a Java web application. The first way is to include the library for a DataSource implementation and create the DataSource by interacting with the library. The second way is to tell the web application container (Tomcat in our case) to create the DataSource for us. This second approach is refered to as container-managed connection pooling because the web application container that hosts the web application creates and destroys to the data source that implements connection pooling. We use container-manager connection pooling in this book.

We configure Tomcat to create a DataSource and provide it to our application through the JNDI ENC name datasource. To do this, replace the contents of ${WORKSPACE}/publisher/publisher.xml with the contents of the following listing. (Remember to replace ${WORKSPACE} with the pathname of your Eclipse workspace.)

publisher.xml

<Context path="/publisher" docBase="${WORKSPACE}/publisher/web">
  <Resource name="datasource" 
            type="javax.sql.DataSource"
            auth="Container"
            maxActive="10"
            maxIdle="3" 
            maxWait="10000"
            username="publisher" 
            password="publisher" 
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/publisher?autoReconnect=true" />
</Context>

To make the above change effective, you need to re-deploy the publisher application. To do this, go to the Tomcat Manager application, undeploy the publisher application, and then deploy the publisher application. After doing this, it is a good idea to check the log files to make sure that deployment was successful. If deployment failed, you need to fix the problem before continuing.

Modify the News Feed Servlet

Since we are using a DataSource to interact with the database, we no longer need the initialization code provided in the NewsFeedServlet. Therefore, delete the init method from NewsFeedServlet.

Add the following static member variable declaration to the news feed servlet.

private static DataSource dataSource;

Organize imports on the above addition to the news feed servlet and select the following class.

We will follow a pattern called dependency injection to set the data source variable. In dependency injection, resources needed by components, such as the data source in our example, are set by initialization code external to the component. The alternative to dependency injection is for the component to acquire its own resources. For this to work in our case, we need to provide a means for external code to set the data source in the news feed servlet. The following code does this.

public static void setDataSource(DataSource dataSource)
{
    NewsFeedServlet.dataSource = dataSource;
}

We make the data source a static property for 2 reasons. First, to remain general, we want allow multiple instances of servlets to be created in our applications (even though typically only a single instance is created), having multiple data source instances passed into these multiple instances defeats the purpose of pooling. Second, the servlets that depend on the data source may not have been created when our initialization code runs. By using a static property, we eliminate the need of doing additional work to assure that all these servlet instances are first created.

The following code currently appears in our news feed servlet.

Connection connection = DriverManager.getConnection(
     "jdbc:mysql://127.0.0.1/publisher", 
     "publisher",
     "publisher");

The above code should be replaced with the following in order to obtain database connections from the connection pool.

Connection connection = dataSource.getConnection();

When using database connection pooling, it is important to close connections and their associated objects when finished with them. If this is not done, the pool of available connections inside the DataSource will become exhausted and application threads will block and wait indefinitely for an available connection.

Create a ServletContextListener to do Initialization

We need to set the DataSource in the DataAccessObject class before the servlets start handling requests. We will do this from a class called Init whose contextInitialized method is called when the publisher web application is loaded by the Web container. For this purpose, create a new class in the publisher.web package called Init that implements the ServletContextListener class. The complete code for this class is given in the following listing.

package publisher.web;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

public class Init implements ServletContextListener {

    private Logger logger = Logger.getLogger(this.getClass());
    
    public void contextDestroyed(ServletContextEvent sce) {
    }
    
    private void contextInitialized2(ServletContext servletContext)
    throws Exception {
       InitialContext enc = new InitialContext();
       Context compContext = (Context) enc.lookup("java:comp/env");
       DataSource dataSource = (DataSource) compContext.lookup("datasource");
       NewsFeedServlet.setDataSource(dataSource);
    }

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        try {
           contextInitialized2(servletContext);
        }
        catch (Exception e)
        {
           logger.error("Initialization failed.", e);
           throw new RuntimeException(e);
        }
        logger.debug("Initialization succeeded.");
    }
}

We have already configured the web container to create a data source for the publisher web application. In order to access this data source, we use the Java Naming and Directory Interface (JNDI) to obtain a reference to the DataSource object created by the container. In the contextInitialized2 method shown above, the first thing we do is create an instance of InitialContext. This is the starting point to locate resources provided through JNDI. In the next line, we narrow the context by calling the lookup method with the name java:comp/env. We named the resulting context enc because it represents the environment naming context (ENC). We then perform a lookup through the ENC on the name datasource to obtain a reference to the data source.

In order for Tomcat to call the contextInitialized method of your Init class, we need to add a listener element to the application's deployment descriptor. Add the following listener element as a child to the web-app element in the deployment descriptor web.xml.

<listener>
   <listener-class>publisher.web.Init</listener-class>
</listener>

Later, we will use the Init class to perform other initializations for the web application.

Depending on your version of Tomcat, you may also need to add the following to the deployment descriptor. This is not needed for Tomcat version 6.

<resource-ref>
      <description>dataSource</description>
      <res-ref-name>datasource</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
</resource-ref>

Test

Make the publisher application reload its deployment descriptor by stoping and starting it through the manager application. Then go to http://localhost:8080/website/home to verify that the website application can still obtain the news feed from the publisher application.

Exercises

(1) Anticipating problems

Introduce the following errors into the code and describe the effects.

Introduce an additional error of your choosing and describe the effects.

back next

Copyright 2007-2009 David Turner and Jinseok Chae. All rights reserved.