egerardus
egerardus

Reputation: 11486

standalone jdbc-pool implementation memory leak

I am trying to implement jdbc-pool in a standalone web app (self contained - not relying on server.xml) so that it can be moved to tomcat installations that may be earlier than 7.0.

I am connecting to MSSQL Server with the sourceforge driver (net.sourceforge.jtds.jdbc.Driver)

Everything runs fine except for this error:

SEVERE: The web application [/jdbc-pool] appears to have started a thread named [[Pool-Cleaner]:Tomcat Connection Pool[1-12524859]] but has failed to stop it. This is very likely to create a memory leak.

Based on this I determined that I need to close the jdbc-pool datasource. I am having trouble with this last line from that post though:

>> If it is configured in the application context, then this simply means you forgot to call DataSource.close on the connection pool when your web application is stopped.

> This is confusing advice because javax.sql.DataSource doesn't have a close() method.

In order to call close, one has to cast it to what ever the data source you are using.

How do I find out what type of datasource I am using and where is the class for it? Can I extract it from the driver jar somehow?

In addition to a servlet which uses the pool, I am using a ServletContextListener so that I can start out with pooled connections immediately from the contextInitialized method. I started adding the code to kill the connection in the contextDestroyed method of this ServletContextListener but got hung up where the question marks are:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

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

public class JdbcPoolListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent myServletContextEvent) {

        // initialize jdbc-pool datasource to start out with pooled connections 
        try {
            Context myContext = (Context) new InitialContext().lookup("java:comp/env");
            DataSource myDataSource = (DataSource) myContext.lookup("jdbc/db");
            myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
        } catch (NamingException e) {
            System.out.println("Error initializing jdbc-pool datasource");
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent myServletContextEvent) {

        // failed attempt to close the data source
        ServletContext myServletContext = myServletContextEvent.getServletContext();
        //DataSource myDataSource = (DataSource) myServletContext.getAttribute("JdbcPool");
        DataSource dataSource = (DataSource)((???) myServletContext.getAttribute(contextAttribute)).getConfiguration().getEnvironment().getDataSource();
        dataSource.close();
        myServletContext.removeAttribute("JdbcPool");

        // deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                System.out.println(String.format("Deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                System.out.println(String.format("Error deregistering driver %s", driver));
                e.printStackTrace();
            }
        }
    }
}

Upvotes: 3

Views: 3960

Answers (1)

egerardus
egerardus

Reputation: 11486

Resolved this, I noticed that the tomcat.jdbc.pool.DataSourceProxy had a close method, so I cast the datasource as a DataSourceProxy, then called closed on it. I no longer get the tomcat memory leak error in the log now.

SOLUTION:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

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

import org.apache.tomcat.jdbc.pool.DataSourceProxy;

public class JdbcPoolListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent myServletContextEvent) {

        // initialize jdbc-pool datasource to start out with pooled connections 
        try {
            Context myContext = (Context) new InitialContext().lookup("java:comp/env");
            DataSource myDataSource = (DataSource) myContext.lookup("jdbc/cf");
            myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
        } catch (NamingException e) {
            System.out.println("Error initializing jdbc-pool datasource");
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent myServletContextEvent) {

    // close datasource from proxy?
    ServletContext myServletContext = myServletContextEvent.getServletContext();
        DataSourceProxy myDataSource = (DataSourceProxy) myServletContext.getAttribute("JdbcPool");
        myDataSource.close();       
        myServletContext.removeAttribute("JdbcPool");

        // deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                System.out.println(String.format("Deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                System.out.println(String.format("Error deregistering driver %s", driver));
                e.printStackTrace();
            }
        }
    }
}

Upvotes: 3

Related Questions